| /* |
| * network.c: |
| * Part of wiringPiD |
| * Copyright (c) 2012-2017 Gordon Henderson |
| *********************************************************************** |
| * This file is part of wiringPi: |
| * https://projects.drogon.net/raspberry-pi/wiringpi/ |
| * |
| * wiringPi is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU Lesser General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * wiringPi is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with wiringPi. If not, see <http://www.gnu.org/licenses/>. |
| *********************************************************************** |
| */ |
| |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <malloc.h> |
| |
| #include <fcntl.h> |
| #include <crypt.h> |
| |
| #include "network.h" |
| |
| #define TRUE (1==1) |
| #define FALSE (!TRUE) |
| |
| // Local data |
| |
| #define SALT_LEN 16 |
| |
| static char salt [SALT_LEN + 1] ; |
| static char *returnedHash = NULL ; |
| static int serverFd = -1 ; |
| |
| // Union for the server Socket Address |
| |
| static union |
| { |
| struct sockaddr_in sin ; |
| struct sockaddr_in6 sin6 ; |
| } serverSockAddr ; |
| |
| // and client address |
| |
| static union |
| { |
| struct sockaddr_in sin ; |
| struct sockaddr_in6 sin6 ; |
| } clientSockAddr ; |
| |
| |
| /* |
| * getClientIP: |
| * Returns a pointer to a static string containing the clients IP address |
| ********************************************************************************* |
| */ |
| |
| char *getClientIP (void) |
| { |
| char buf [INET6_ADDRSTRLEN] ; |
| static char ipAddress [1024] ; |
| |
| if (clientSockAddr.sin.sin_family == AF_INET) // IPv4 |
| { |
| if (snprintf (ipAddress, 1024, "IPv4: %s", |
| inet_ntop (clientSockAddr.sin.sin_family, (void *)&clientSockAddr.sin.sin_addr, buf, sizeof (buf))) == 1024) |
| strcpy (ipAddress, "Too long") ; |
| } |
| else // IPv6 |
| { |
| if (clientSockAddr.sin.sin_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&clientSockAddr.sin6.sin6_addr)) |
| { |
| if (snprintf (ipAddress, 1024, "IPv4in6: %s", |
| inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024) |
| strcpy (ipAddress, "Too long") ; |
| } |
| else |
| { |
| if (snprintf (ipAddress, 1024, "IPv6: %s", |
| inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024) |
| strcpy (ipAddress, "Too long") ; |
| } |
| } |
| |
| return ipAddress ; |
| } |
| |
| |
| |
| /* |
| * clientPstr: clientPrintf: |
| * Print over a network socket |
| ********************************************************************************* |
| */ |
| |
| static int clientPstr (int fd, char *s) |
| { |
| int len = strlen (s) ; |
| return (write (fd, s, len) == len) ? 0 : -1 ; |
| } |
| |
| static int clientPrintf (const int fd, const char *message, ...) |
| { |
| va_list argp ; |
| char buffer [1024] ; |
| |
| va_start (argp, message) ; |
| vsnprintf (buffer, 1023, message, argp) ; |
| va_end (argp) ; |
| |
| return clientPstr (fd, buffer) ; |
| } |
| |
| |
| /* |
| * sendGreeting: |
| * Send some text to the client device |
| ********************************************************************************* |
| */ |
| |
| int sendGreeting (int clientFd) |
| { |
| if (clientPrintf (clientFd, "200 Welcome to wiringPiD - http://wiringpi.com/\n") < 0) |
| return -1 ; |
| |
| return clientPrintf (clientFd, "200 Connecting from: %s\n", getClientIP ()) ; |
| } |
| |
| |
| /* |
| * getSalt: |
| * Create a random 'salt' value for the password encryption process |
| ********************************************************************************* |
| */ |
| |
| static int getSalt (char drySalt []) |
| { |
| static const char *seaDog = "abcdefghijklmnopqrstuvwxyz" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "0123456789/." ; |
| |
| unsigned char wetSalt [SALT_LEN] ; |
| int i, fd ; |
| |
| if ((fd = open ("/dev/urandom", O_RDONLY)) < 0) |
| return fd ; |
| |
| if (read (fd, wetSalt, SALT_LEN) != SALT_LEN) |
| return -1 ; |
| |
| close (fd) ; |
| |
| for (i = 0 ; i < SALT_LEN ; ++i) |
| drySalt [i] = seaDog [wetSalt [i] & 63] ; |
| |
| drySalt [SALT_LEN] = 0 ; |
| |
| return 0 ; |
| } |
| |
| |
| /* |
| * sendChallenge: |
| * Create and send our salt (aka nonce) to the remote device |
| ********************************************************************************* |
| */ |
| |
| int sendChallenge (int clientFd) |
| { |
| if (getSalt (salt) < 0) |
| return -1 ; |
| |
| return clientPrintf (clientFd, "Challenge %s\n", salt) ; |
| } |
| |
| |
| /* |
| * getResponse: |
| * Read the encrypted password from the remote device. |
| ********************************************************************************* |
| */ |
| |
| |
| int getResponse (int clientFd) |
| { |
| char reply [1024] ; |
| int len ; |
| |
| // Being sort of lazy about this. I'm expecting an SHA-512 hash back and these |
| // are exactly 86 characters long, so no reason not to, I guess... |
| |
| len = 86 ; |
| |
| if (setsockopt (clientFd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) |
| return -1 ; |
| |
| len = recv (clientFd, reply, 86, 0) ; |
| if (len != 86) |
| return -1 ; |
| |
| reply [len] = 0 ; |
| |
| if ((returnedHash = malloc (len + 1)) == NULL) |
| return -1 ; |
| |
| strcpy (returnedHash, reply) ; |
| |
| return 0 ; |
| } |
| |
| |
| /* |
| * passwordMatch: |
| * See if there's a match. If not, we simply dump them. |
| ********************************************************************************* |
| */ |
| |
| int passwordMatch (const char *password) |
| { |
| char *encrypted ; |
| char salted [1024] ; |
| |
| sprintf (salted, "$6$%s$", salt) ; |
| |
| encrypted = crypt (password, salted) ; |
| |
| // 20: $6$ then 16 characters of salt, then $ |
| // 86 is the length of an SHA-512 hash |
| |
| return strncmp (encrypted + 20, returnedHash, 86) == 0 ; |
| } |
| |
| |
| /* |
| * setupServer: |
| * Do what's needed to create a local server socket instance that can listen |
| * on both IPv4 and IPv6 interfaces. |
| ********************************************************************************* |
| */ |
| |
| int setupServer (int serverPort) |
| { |
| socklen_t clientSockAddrSize = sizeof (clientSockAddr) ; |
| |
| int on = 1 ; |
| int family ; |
| socklen_t serverSockAddrSize ; |
| int clientFd ; |
| |
| // Try to create an IPv6 socket |
| |
| serverFd = socket (PF_INET6, SOCK_STREAM, 0) ; |
| |
| // If it didn't work, then fall-back to IPv4. |
| |
| if (serverFd < 0) |
| { |
| if ((serverFd = socket (PF_INET, SOCK_STREAM, 0)) < 0) |
| return -1 ; |
| |
| family = AF_INET ; |
| serverSockAddrSize = sizeof (struct sockaddr_in) ; |
| } |
| else // We got an IPv6 socket |
| { |
| family = AF_INET6 ; |
| serverSockAddrSize = sizeof (struct sockaddr_in6) ; |
| } |
| |
| if (setsockopt (serverFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) |
| return -1 ; |
| |
| // Setup the servers socket address - cope with IPv4 and v6. |
| |
| memset (&serverSockAddr, 0, sizeof (serverSockAddr)) ; |
| switch (family) |
| { |
| case AF_INET: |
| serverSockAddr.sin.sin_family = AF_INET ; |
| serverSockAddr.sin.sin_addr.s_addr = htonl (INADDR_ANY) ; |
| serverSockAddr.sin.sin_port = htons (serverPort) ; |
| break; |
| |
| case AF_INET6: |
| serverSockAddr.sin6.sin6_family = AF_INET6 ; |
| serverSockAddr.sin6.sin6_addr = in6addr_any ; |
| serverSockAddr.sin6.sin6_port = htons (serverPort) ; |
| } |
| |
| // Bind, listen and accept |
| |
| if (bind (serverFd, (struct sockaddr *)&serverSockAddr, serverSockAddrSize) < 0) |
| return -1 ; |
| |
| if (listen (serverFd, 4) < 0) // Really only going to talk to one client at a time... |
| return -1 ; |
| |
| if ((clientFd = accept (serverFd, (struct sockaddr *)&clientSockAddr, &clientSockAddrSize)) < 0) |
| return -1 ; |
| |
| return clientFd ; |
| } |
| |
| |
| /* |
| * closeServer: |
| ********************************************************************************* |
| */ |
| |
| void closeServer (int clientFd) |
| { |
| if (serverFd != -1) close (serverFd) ; |
| if (clientFd != -1) close (clientFd) ; |
| serverFd = clientFd = -1 ; |
| } |