/* * drcNet.h: * Extend wiringPi with the DRC Network protocol (e.g. to another Pi) * Copyright (c) 2016-2017 Gordon Henderson *********************************************************************** * This file is part of wiringPi: * https://github.com/WiringPi/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 . *********************************************************************** */ #include #include #include #include #include #include #include #include #include #include #include "wiringPi.h" #include "drcNet.h" #include "../wiringPiD/drcNetCmd.h" /* * remoteReadline: * Read in a line of data from the remote server, ending with a newline * character which is not stored. Returns the length or < 0 on * any sort of failure. ********************************************************************************* */ static int remoteReadline (int fd, char *buf, int max) { int len = 0; char c; for (;;) { if (read (fd, &c, 1) < 1) return -1; if (c == '\n') return len; *buf++ = c; if (++len == max) return len; } } /* * getChallenge: * Read in lines from the remote site until we get one identified * as the challenge. This line contains the password salt. ********************************************************************************* */ static char *getChallenge (int fd) { static char buf [1024]; int num; for (;;) { if ((num = remoteReadline (fd, buf, 1023)) < 0) return NULL; buf [num] = 0; if (strncmp (buf, "Challenge ", 10) == 0) return &buf [10]; } } /* * authenticate: * Read in the challenge from the server, use it to encrypt our password * and send it back to the server. Wait for a reply back from the server * to say that we're good to go. * The server will simply disconnect on a bad response. No 3 chances here. ********************************************************************************* */ static int authenticate (int fd, const char *pass) { char *challenge; char *encrypted; char salted [1024+4]; // Need to hold 4 more chars - see sprintf below if ((challenge = getChallenge (fd)) == NULL) return -1; sprintf (salted, "$6$%s$", challenge); encrypted = crypt (pass, salted); // Assertion: // The '20' comes from the $6$ then the 16 characters of the salt, // then the terminating $. if (strncmp (encrypted, salted, 20) != 0) { errno = EBADE; return -1; } // 86 characters is the length of the SHA-256 hash if (write (fd, encrypted + 20, 86) == 86) return 0; else return -1; } /* * _drcSetupNet: * Do the hard work of establishing a network connection and authenticating * the password. ********************************************************************************* */ int _drcSetupNet (const char *ipAddress, const char *port, const char *password) { struct addrinfo hints; struct addrinfo *result, *rp; struct in6_addr serveraddr; int remoteFd; // Start by seeing if we've been given a (textual) numeric IP address // which will save lookups in getaddrinfo() memset (&hints, 0, sizeof (hints)); hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; if (inet_pton (AF_INET, ipAddress, &serveraddr) == 1) // Valid IPv4 { hints.ai_family = AF_INET; hints.ai_flags |= AI_NUMERICHOST; } else { if (inet_pton (AF_INET6, ipAddress, &serveraddr) == 1) // Valid IPv6 { hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; } } // Now use getaddrinfo() with the newly supplied hints if (getaddrinfo (ipAddress, port, &hints, &result) != 0) return -1; // Now try each address in-turn until we get one that connects... for (rp = result; rp != NULL; rp = rp->ai_next) { if ((remoteFd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) continue; if (connect (remoteFd, rp->ai_addr, rp->ai_addrlen) < 0) continue; if (authenticate (remoteFd, password) < 0) { close (remoteFd); errno = EACCES; // Permission denied return -1; } else return remoteFd; } errno = EHOSTUNREACH; // Host unreachable - may not be right, but good enough return -1; // Nothing connected } /* * myPinMode: * Change the pin mode on the remote DRC device ********************************************************************************* */ static void myPinMode (struct wiringPiNodeStruct *node, int pin, int mode) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_PIN_MODE; cmd.data = mode; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); } /* * myPullUpDnControl: ********************************************************************************* */ static void myPullUpDnControl (struct wiringPiNodeStruct *node, int pin, int mode) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_PULL_UP_DN; cmd.data = mode; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); } /* * myDigitalWrite: ********************************************************************************* */ static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int value) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_DIGITAL_WRITE; cmd.data = value; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); } /* * myAnalogWrite: ********************************************************************************* */ static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_ANALOG_WRITE; cmd.data = value; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); } /* * myPwmWrite: ********************************************************************************* */ static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_PWM_WRITE; cmd.data = value; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); } /* * myAnalogRead: ********************************************************************************* */ static int myAnalogRead (struct wiringPiNodeStruct *node, int pin) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_ANALOG_READ; cmd.data = 0; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); return cmd.data; } /* * myDigitalRead: ********************************************************************************* */ static int myDigitalRead (struct wiringPiNodeStruct *node, int pin) { struct drcNetComStruct cmd; cmd.pin = pin - node->pinBase; cmd.cmd = DRCN_DIGITAL_READ; cmd.data = 0; (void)send (node->fd, &cmd, sizeof (cmd), 0); (void)recv (node->fd, &cmd, sizeof (cmd), 0); return cmd.data; } /* * drcNet: * Create a new instance of an DRC GPIO interface. * Could be a variable nunber of pins here - we might not know in advance. ********************************************************************************* */ int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password) { int fd, len; struct wiringPiNodeStruct *node; if ((fd = _drcSetupNet (ipAddress, port, password)) < 0) return FALSE; len = sizeof (struct drcNetComStruct); if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) return FALSE; node = wiringPiNewNode (pinBase, numPins); node->fd = fd; node->pinMode = myPinMode; node->pullUpDnControl = myPullUpDnControl; node->analogRead = myAnalogRead; node->analogWrite = myAnalogWrite; node->digitalRead = myDigitalRead; node->digitalWrite = myDigitalWrite; node->pwmWrite = myPwmWrite; return TRUE; }