A recent compiler complains about the sprintf() in authenticate() being able to write up to 1023 bytes into challenge, which is not possible given that getChallenge() returns a string 10 bytes smaller than whatever string is in its 1024 byte buffer. Reducing the size of the buffer in getChallenge() quiets the warning. It should not affect anything else, as the challenge string is used as a salt that must be <= 96 bits.
406 lines
9.8 KiB
C
406 lines
9.8 KiB
C
/*
|
|
* 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 [512] ;
|
|
int num ;
|
|
|
|
for (;;)
|
|
{
|
|
if ((num = remoteReadline (fd, buf, 511)) < 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 ;
|
|
}
|