- /*
- * 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 <http://www.gnu.org/licenses/>.
- ***********************************************************************
- */
-
- #include <stdio.h>
- #include <stdint.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <string.h>
- #include <errno.h>
- #include <crypt.h>
-
-
- #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;
- }
|