Gordons Projects

--> Projects Top-Level GIT

Bumped the version to 2.40 - correctly this time, I hope.
[wiringPi] / wiringPiD / network.c
1 /*
2  * network.c:
3  *      Part of wiringPiD
4  *      Copyright (c) 2012-2017 Gordon Henderson
5  ***********************************************************************
6  * This file is part of wiringPi:
7  *      https://projects.drogon.net/raspberry-pi/wiringpi/
8  *
9  *    wiringPi is free software: you can redistribute it and/or modify
10  *    it under the terms of the GNU Lesser General Public License as published by
11  *    the Free Software Foundation, either version 3 of the License, or
12  *    (at your option) any later version.
13  *
14  *    wiringPi is distributed in the hope that it will be useful,
15  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *    GNU Lesser General Public License for more details.
18  *
19  *    You should have received a copy of the GNU Lesser General Public License
20  *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
21  ***********************************************************************
22  */
23
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <malloc.h>
33
34 #include <fcntl.h>
35 #include <crypt.h>
36
37 #include "network.h"
38
39 #define TRUE    (1==1)
40 #define FALSE   (!TRUE)
41
42 // Local data
43
44 #define SALT_LEN        16
45
46 static char salt [SALT_LEN + 1] ;
47 static char *returnedHash = NULL ;
48 static int serverFd = -1 ;
49
50 // Union for the server Socket Address
51
52 static union
53 {
54   struct sockaddr_in  sin ;
55   struct sockaddr_in6 sin6 ;
56 } serverSockAddr ; 
57
58 // and client address
59
60 static union
61 {
62   struct sockaddr_in  sin ;
63   struct sockaddr_in6 sin6 ;
64 } clientSockAddr ;
65
66
67 /*
68  * getClientIP:
69  *      Returns a pointer to a static string containing the clients IP address
70  *********************************************************************************
71  */
72
73 char *getClientIP (void)
74 {
75   char buf [INET6_ADDRSTRLEN] ;
76   static char ipAddress [1024] ;
77
78   if (clientSockAddr.sin.sin_family == AF_INET) // IPv4
79   {
80     if (snprintf (ipAddress, 1024, "IPv4: %s", 
81         inet_ntop (clientSockAddr.sin.sin_family, (void *)&clientSockAddr.sin.sin_addr, buf, sizeof (buf))) == 1024)
82       strcpy (ipAddress, "Too long") ;
83   }
84   else                                          // IPv6
85   {
86     if (clientSockAddr.sin.sin_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&clientSockAddr.sin6.sin6_addr))
87     {
88       if (snprintf (ipAddress, 1024, "IPv4in6: %s", 
89         inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024)
90       strcpy (ipAddress, "Too long") ;
91     }
92     else
93     {
94       if (snprintf (ipAddress, 1024, "IPv6: %s", 
95         inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024)
96       strcpy (ipAddress, "Too long") ;
97     }
98   }
99
100   return ipAddress ;
101 }
102
103
104
105 /*
106  * clientPstr: clientPrintf:
107  *      Print over a network socket
108  *********************************************************************************
109  */
110
111 static int clientPstr (int fd, char *s)
112 {
113   int len = strlen (s) ;
114   return (write (fd, s, len) == len) ? 0 : -1 ;
115 }
116
117 static int clientPrintf (const int fd, const char *message, ...)
118 {
119   va_list argp ;
120   char buffer [1024] ;
121
122   va_start (argp, message) ;
123     vsnprintf (buffer, 1023, message, argp) ;
124   va_end (argp) ;
125
126   return clientPstr (fd, buffer) ;
127 }
128
129
130 /*
131  * sendGreeting:
132  *      Send some text to the client device
133  *********************************************************************************
134  */
135
136 int sendGreeting (int clientFd)
137 {
138   if (clientPrintf (clientFd, "200 Welcome to wiringPiD - http://wiringpi.com/\n") < 0)
139     return -1 ;
140
141   return clientPrintf (clientFd, "200 Connecting from: %s\n", getClientIP ()) ;
142 }
143
144
145 /*
146  * getSalt:
147  *      Create a random 'salt' value for the password encryption process
148  *********************************************************************************
149  */
150
151 static int getSalt (char drySalt [])
152 {
153   static const char *seaDog =   "abcdefghijklmnopqrstuvwxyz"
154                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
155                                 "0123456789/." ;
156
157   unsigned char wetSalt [SALT_LEN] ;
158   int i, fd ;
159
160   if ((fd = open ("/dev/urandom", O_RDONLY)) < 0)
161     return fd ;
162
163   if (read (fd, wetSalt, SALT_LEN) != SALT_LEN)
164     return -1 ;
165
166   close (fd) ;
167
168   for (i = 0 ; i < SALT_LEN ; ++i)
169     drySalt [i] = seaDog [wetSalt [i] & 63] ;
170     
171   drySalt [SALT_LEN] = 0 ;
172
173   return 0 ;
174 }
175
176
177 /*
178  * sendChallenge:
179  *      Create and send our salt (aka nonce) to the remote device
180  *********************************************************************************
181  */
182
183 int sendChallenge (int clientFd)
184 {
185   if (getSalt (salt) < 0)
186     return -1 ;
187
188   return clientPrintf (clientFd, "Challenge %s\n", salt) ;
189 }
190
191
192 /*
193  * getResponse:
194  *      Read the encrypted password from the remote device.
195  *********************************************************************************
196  */
197
198
199 int getResponse (int clientFd)
200 {
201   char reply [1024] ;
202   int len ;
203
204 // Being sort of lazy about this. I'm expecting an SHA-512 hash back and these
205 //      are exactly 86 characters long, so no reason not to, I guess...
206
207   len = 86 ;
208
209   if (setsockopt (clientFd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0)
210     return -1 ;
211
212   len = recv (clientFd, reply, 86, 0) ;
213   if (len != 86)
214     return -1 ;
215
216   reply [len] = 0 ;
217
218   if ((returnedHash = malloc (len + 1)) == NULL)
219     return -1 ;
220
221   strcpy (returnedHash, reply) ;
222
223   return 0 ;
224 }
225
226
227 /*
228  * passwordMatch:
229  *      See if there's a match. If not, we simply dump them.
230  *********************************************************************************
231  */
232
233 int passwordMatch (const char *password)
234 {
235   char *encrypted ;
236   char salted [1024] ;
237
238   sprintf (salted, "$6$%s$", salt) ;
239
240   encrypted = crypt (password, salted) ;
241
242 // 20: $6$ then 16 characters of salt, then $
243 // 86 is the length of an SHA-512 hash
244
245   return strncmp (encrypted + 20, returnedHash, 86) == 0 ;
246 }
247
248
249 /* 
250  * setupServer:
251  *      Do what's needed to create a local server socket instance that can listen
252  *      on both IPv4 and IPv6 interfaces.
253  *********************************************************************************
254  */
255
256 int setupServer (int serverPort)
257 {
258   socklen_t clientSockAddrSize = sizeof (clientSockAddr) ;
259
260   int on = 1 ;
261   int family ;
262   socklen_t serverSockAddrSize ;
263   int clientFd ;
264
265 // Try to create an IPv6 socket
266
267   serverFd = socket (PF_INET6, SOCK_STREAM, 0) ;
268
269 // If it didn't work, then fall-back to IPv4.
270
271   if (serverFd < 0)
272   {
273     if ((serverFd = socket (PF_INET, SOCK_STREAM, 0)) < 0)
274       return -1 ;
275
276     family             = AF_INET ;
277     serverSockAddrSize = sizeof (struct sockaddr_in) ;
278   }
279   else          // We got an IPv6 socket
280   {
281     family             = AF_INET6 ;
282     serverSockAddrSize = sizeof (struct sockaddr_in6) ;
283   }
284
285   if (setsockopt (serverFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
286     return -1 ;
287
288 // Setup the servers socket address - cope with IPv4 and v6.
289
290   memset (&serverSockAddr, 0, sizeof (serverSockAddr)) ;
291   switch (family)
292   {
293     case AF_INET:
294       serverSockAddr.sin.sin_family      = AF_INET ;
295       serverSockAddr.sin.sin_addr.s_addr = htonl (INADDR_ANY) ;
296       serverSockAddr.sin.sin_port        = htons (serverPort) ;
297       break;
298
299     case AF_INET6:
300       serverSockAddr.sin6.sin6_family = AF_INET6 ;
301       serverSockAddr.sin6.sin6_addr   = in6addr_any ;
302       serverSockAddr.sin6.sin6_port   = htons (serverPort) ;
303   }
304
305 // Bind, listen and accept
306
307   if (bind (serverFd, (struct sockaddr *)&serverSockAddr, serverSockAddrSize) < 0)
308     return -1 ;
309
310   if (listen (serverFd, 4) < 0) // Really only going to talk to one client at a time...
311     return -1 ;
312
313   if ((clientFd = accept (serverFd, (struct sockaddr *)&clientSockAddr, &clientSockAddrSize)) < 0)
314     return -1 ;
315
316   return clientFd ;
317 }
318
319
320 /*
321  * closeServer:
322  *********************************************************************************
323  */
324
325 void closeServer (int clientFd)
326 {
327   if (serverFd != -1) close (serverFd) ;
328   if (clientFd != -1) close (clientFd) ;
329   serverFd = clientFd = -1 ;
330 }