Gordons Projects

--> Projects Top-Level GIT

Bumped the version to 2.40 - correctly this time, I hope.
[wiringPi] / wiringPi / drcNet.c
1 /*
2  * drcNet.h:
3  *      Extend wiringPi with the DRC Network protocol (e.g. to another Pi)
4  *      Copyright (c) 2016-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
11  *    published by the Free Software Foundation, either version 3 of the
12  *    License, or (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
20  *    License along with wiringPi.
21  *    If not, see <http://www.gnu.org/licenses/>.
22  ***********************************************************************
23  */
24
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31 #include <netdb.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <crypt.h>
35
36
37 #include "wiringPi.h"
38 #include "drcNet.h"
39 #include "../wiringPiD/drcNetCmd.h"
40
41
42 /*
43  * remoteReadline:
44  *      Read in a line of data from the remote server, ending with a newline
45  *      character which is not stored. Returns the length or < 0 on
46  *      any sort of failure.
47  *********************************************************************************
48  */
49
50 static int remoteReadline (int fd, char *buf, int max)
51 {
52   int  len = 0 ;
53   char c ;
54
55   for (;;)
56   {
57     if (read (fd, &c, 1) < 1)
58       return -1 ;
59
60     if (c == '\n')
61       return len ;
62
63     *buf++ = c ;
64     if (++len == max)
65       return len ;
66   }
67 }
68
69
70 /*
71  * getChallenge:
72  *      Read in lines from the remote site until we get one identified
73  *      as the challenge. This line contains the password salt.
74  *********************************************************************************
75  */
76
77 static char *getChallenge (int fd)
78 {
79   static char buf [1024] ;
80   int num ;
81
82   for (;;)
83   {
84     if ((num = remoteReadline (fd, buf, 1023)) < 0)
85       return NULL ;
86     buf [num] = 0 ;
87
88     if (strncmp (buf, "Challenge ", 10) == 0)
89       return &buf [10] ;
90   }
91 }
92
93
94 /*
95  * authenticate:
96  *      Read in the challenge from the server, use it to encrypt our password
97  *      and send it back to the server. Wait for a reply back from the server
98  *      to say that we're good to go.
99  *      The server will simply disconnect on a bad response. No 3 chances here.
100  *********************************************************************************
101  */
102
103 static int authenticate (int fd, const char *pass)
104 {
105   char *challenge ;
106   char *encrypted ;
107   char salted [1024] ;
108
109   if ((challenge = getChallenge (fd)) == NULL)
110     return -1 ;
111
112   sprintf (salted, "$6$%s$", challenge) ;
113   encrypted = crypt (pass, salted) ;
114   
115 // This is an assertion, or sanity check on my part...
116 //      The '20' comes from the $6$ then the 16 characters of the salt,
117 //      then the terminating $.
118
119   if (strncmp (encrypted, salted, 20) != 0)
120   {
121     errno = EBADE ;
122     return -1 ;
123   }
124
125 // 86 characters is the length of the SHA-256 hash
126
127   if (write (fd, encrypted + 20, 86) == 86)
128     return 0 ;
129   else
130     return -1 ;
131 }
132
133
134 /*
135  * _drcSetupNet:
136  *      Do the hard work of establishing a network connection and authenticating
137  *      the password.
138  *********************************************************************************
139  */
140
141 int _drcSetupNet (const char *ipAddress, const char *port, const char *password)
142 {
143   struct addrinfo hints;
144   struct addrinfo *result, *rp ;
145   struct in6_addr serveraddr ;
146   int remoteFd ;
147
148 // Start by seeing if we've been given a (textual) numeric IP address
149 //      which will save lookups in getaddrinfo()
150
151   memset (&hints, 0, sizeof (hints)) ;
152   hints.ai_flags    = AI_NUMERICSERV ;
153   hints.ai_family   = AF_UNSPEC ;
154   hints.ai_socktype = SOCK_STREAM ;
155   hints.ai_protocol = 0 ;
156
157   if (inet_pton (AF_INET, ipAddress, &serveraddr) == 1)         // Valid IPv4
158   {
159     hints.ai_family = AF_INET ;
160     hints.ai_flags |= AI_NUMERICHOST ;
161   }
162   else
163   {
164     if (inet_pton (AF_INET6, ipAddress, &serveraddr) == 1)      // Valid IPv6
165     {
166       hints.ai_family = AF_INET6 ;
167       hints.ai_flags |= AI_NUMERICHOST ;
168     }
169   }
170
171 // Now use getaddrinfo() with the newly supplied hints
172
173   if (getaddrinfo (ipAddress, port, &hints, &result) != 0)
174     return -1 ;
175
176 // Now try each address in-turn until we get one that connects...
177
178   for (rp = result; rp != NULL; rp = rp->ai_next)
179   {
180     if ((remoteFd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
181       continue ;
182
183     if (connect (remoteFd, rp->ai_addr, rp->ai_addrlen) < 0)
184       continue ;
185
186     if (authenticate (remoteFd, password) < 0)
187     {
188       close (remoteFd) ;
189       errno = EACCES ;          // Permission denied
190       return -1 ;
191     }
192     else
193       return remoteFd ;
194   }
195
196   errno = EHOSTUNREACH ;        // Host unreachable - may not be right, but good enough
197   return -1 ; // Nothing connected
198 }
199
200
201 /*
202  * myPinMode:
203  *      Change the pin mode on the remote DRC device
204  *********************************************************************************
205  */
206
207 static void myPinMode (struct wiringPiNodeStruct *node, int pin, int mode)
208 {
209   struct drcNetComStruct cmd ;
210
211   cmd.pin  = pin - node->pinBase ;
212   cmd.cmd  = DRCN_PIN_MODE ;
213   cmd.data = mode ;
214
215   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
216   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
217 }
218
219
220 /*
221  * myPullUpDnControl:
222  *********************************************************************************
223  */
224
225 static void myPullUpDnControl (struct wiringPiNodeStruct *node, int pin, int mode)
226 {
227   struct drcNetComStruct cmd ;
228
229   cmd.pin  = pin - node->pinBase ;
230   cmd.cmd  = DRCN_PULL_UP_DN ;
231   cmd.data = mode ;
232
233   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
234   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
235 }
236
237
238 /*
239  * myDigitalWrite:
240  *********************************************************************************
241  */
242
243 static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int value)
244 {
245   struct drcNetComStruct cmd ;
246
247   cmd.pin  = pin - node->pinBase ;
248   cmd.cmd  = DRCN_DIGITAL_WRITE ;
249   cmd.data = value ;
250
251   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
252   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
253 }
254
255
256 /*
257  * myDigitalWrite8:
258  *********************************************************************************
259
260 static void myDigitalWrite8 (struct wiringPiNodeStruct *node, int pin, int value)
261 {
262   struct drcNetComStruct cmd ;
263
264   cmd.pin  = pin - node->pinBase ;
265   cmd.cmd  = DRCN_DIGITAL_WRITE8 ;
266   cmd.data = value ;
267
268   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
269   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
270 }
271  */
272
273
274 /*
275  * myAnalogWrite:
276  *********************************************************************************
277  */
278
279 static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value)
280 {
281   struct drcNetComStruct cmd ;
282
283   cmd.pin  = pin - node->pinBase ;
284   cmd.cmd  = DRCN_ANALOG_WRITE ;
285   cmd.data = value ;
286
287   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
288   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
289 }
290
291
292 /*
293  * myPwmWrite:
294  *********************************************************************************
295  */
296
297 static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value)
298 {
299   struct drcNetComStruct cmd ;
300
301   cmd.pin  = pin - node->pinBase ;
302   cmd.cmd  = DRCN_PWM_WRITE ;
303   cmd.data = value ;
304
305   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
306   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
307 }
308
309
310 /*
311  * myAnalogRead:
312  *********************************************************************************
313  */
314
315 static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
316 {
317   struct drcNetComStruct cmd ;
318
319   cmd.pin  = pin - node->pinBase ;
320   cmd.cmd  = DRCN_ANALOG_READ ;
321   cmd.data = 0 ;
322
323   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
324   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
325
326   return cmd.data ;
327 }
328
329
330 /*
331  * myDigitalRead:
332  *********************************************************************************
333  */
334
335 static int myDigitalRead (struct wiringPiNodeStruct *node, int pin)
336 {
337   struct drcNetComStruct cmd ;
338
339   cmd.pin  = pin - node->pinBase ;
340   cmd.cmd  = DRCN_DIGITAL_READ ;
341   cmd.data = 0 ;
342
343   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
344   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
345
346   return cmd.data ;
347 }
348
349
350 /*
351  * myDigitalRead8:
352  *********************************************************************************
353
354 static unsigned int myDigitalRead8 (struct wiringPiNodeStruct *node, int pin)
355 {
356   struct drcNetComStruct cmd ;
357
358   cmd.pin  = pin - node->pinBase ;
359   cmd.cmd  = DRCN_DIGITAL_READ8 ;
360   cmd.data = 0 ;
361
362   (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
363   (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
364
365   return cmd.data ;
366 }
367  */
368
369
370 /*
371  * drcNet:
372  *      Create a new instance of an DRC GPIO interface.
373  *      Could be a variable nunber of pins here - we might not know in advance.
374  *********************************************************************************
375  */
376
377 int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password)
378 {
379   int fd, len ;
380   struct wiringPiNodeStruct *node ;
381
382   if ((fd = _drcSetupNet (ipAddress, port, password)) < 0)
383     return FALSE ;
384
385   len = sizeof (struct drcNetComStruct) ;
386
387   if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0)
388     return FALSE ;
389
390   node = wiringPiNewNode (pinBase, numPins) ;
391
392   node->fd               = fd ;
393   node->pinMode          = myPinMode ;
394   node->pullUpDnControl  = myPullUpDnControl ;
395   node->analogRead       = myAnalogRead ;
396   node->analogRead       = myAnalogRead ;
397   node->analogWrite      = myAnalogWrite ;
398   node->digitalRead      = myDigitalRead ;
399   node->digitalWrite     = myDigitalWrite ;
400 //node->digitalRead8     = myDigitalRead8 ;
401 //node->digitalWrite8    = myDigitalWrite8 ;
402   node->pwmWrite         = myPwmWrite ;
403
404   return TRUE ;
405 }