|
@@ -0,0 +1,405 @@
|
|
|
+/*
|
|
|
+ * 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://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 <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] ;
|
|
|
+
|
|
|
+ if ((challenge = getChallenge (fd)) == NULL)
|
|
|
+ return -1 ;
|
|
|
+
|
|
|
+ sprintf (salted, "$6$%s$", challenge) ;
|
|
|
+ encrypted = crypt (pass, salted) ;
|
|
|
+
|
|
|
+// This is an assertion, or sanity check on my part...
|
|
|
+// 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) ;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * myDigitalWrite8:
|
|
|
+ *********************************************************************************
|
|
|
+
|
|
|
+static void myDigitalWrite8 (struct wiringPiNodeStruct *node, int pin, int value)
|
|
|
+{
|
|
|
+ struct drcNetComStruct cmd ;
|
|
|
+
|
|
|
+ cmd.pin = pin - node->pinBase ;
|
|
|
+ cmd.cmd = DRCN_DIGITAL_WRITE8 ;
|
|
|
+ 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 ;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * myDigitalRead8:
|
|
|
+ *********************************************************************************
|
|
|
+
|
|
|
+static unsigned int myDigitalRead8 (struct wiringPiNodeStruct *node, int pin)
|
|
|
+{
|
|
|
+ struct drcNetComStruct cmd ;
|
|
|
+
|
|
|
+ cmd.pin = pin - node->pinBase ;
|
|
|
+ cmd.cmd = DRCN_DIGITAL_READ8 ;
|
|
|
+ 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->analogRead = myAnalogRead ;
|
|
|
+ node->analogWrite = myAnalogWrite ;
|
|
|
+ node->digitalRead = myDigitalRead ;
|
|
|
+ node->digitalWrite = myDigitalWrite ;
|
|
|
+//node->digitalRead8 = myDigitalRead8 ;
|
|
|
+//node->digitalWrite8 = myDigitalWrite8 ;
|
|
|
+ node->pwmWrite = myPwmWrite ;
|
|
|
+
|
|
|
+ return TRUE ;
|
|
|
+}
|