/*
* 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;
}