You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

358 regels
8.8 KiB

  1. /*
  2. * drcNet.h:
  3. * Extend wiringPi with the DRC Network protocol (e.g. to another Pi)
  4. * Copyright (c) 2016-2017 Gordon Henderson
  5. ***********************************************************************
  6. * This file is part of wiringPi:
  7. * https://github.com/WiringPi/WiringPi/
  8. *
  9. * wiringPi is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Lesser General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * wiringPi is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with wiringPi.
  21. * If not, see <http://www.gnu.org/licenses/>.
  22. ***********************************************************************
  23. */
  24. #include <stdio.h>
  25. #include <stdint.h>
  26. #include <unistd.h>
  27. #include <sys/types.h>
  28. #include <sys/socket.h>
  29. #include <arpa/inet.h>
  30. #include <netdb.h>
  31. #include <string.h>
  32. #include <errno.h>
  33. #include <crypt.h>
  34. #include "wiringPi.h"
  35. #include "drcNet.h"
  36. #include "../wiringPiD/drcNetCmd.h"
  37. /*
  38. * remoteReadline:
  39. * Read in a line of data from the remote server, ending with a newline
  40. * character which is not stored. Returns the length or < 0 on
  41. * any sort of failure.
  42. *********************************************************************************
  43. */
  44. static int remoteReadline (int fd, char *buf, int max)
  45. {
  46. int len = 0;
  47. char c;
  48. for (;;)
  49. {
  50. if (read (fd, &c, 1) < 1)
  51. return -1;
  52. if (c == '\n')
  53. return len;
  54. *buf++ = c;
  55. if (++len == max)
  56. return len;
  57. }
  58. }
  59. /*
  60. * getChallenge:
  61. * Read in lines from the remote site until we get one identified
  62. * as the challenge. This line contains the password salt.
  63. *********************************************************************************
  64. */
  65. static char *getChallenge (int fd)
  66. {
  67. static char buf [1024];
  68. int num;
  69. for (;;)
  70. {
  71. if ((num = remoteReadline (fd, buf, 1023)) < 0)
  72. return NULL;
  73. buf [num] = 0;
  74. if (strncmp (buf, "Challenge ", 10) == 0)
  75. return &buf [10];
  76. }
  77. }
  78. /*
  79. * authenticate:
  80. * Read in the challenge from the server, use it to encrypt our password
  81. * and send it back to the server. Wait for a reply back from the server
  82. * to say that we're good to go.
  83. * The server will simply disconnect on a bad response. No 3 chances here.
  84. *********************************************************************************
  85. */
  86. static int authenticate (int fd, const char *pass)
  87. {
  88. char *challenge;
  89. char *encrypted;
  90. char salted [1024+4]; // Need to hold 4 more chars - see sprintf below
  91. if ((challenge = getChallenge (fd)) == NULL)
  92. return -1;
  93. sprintf (salted, "$6$%s$", challenge);
  94. encrypted = crypt (pass, salted);
  95. // Assertion:
  96. // The '20' comes from the $6$ then the 16 characters of the salt,
  97. // then the terminating $.
  98. if (strncmp (encrypted, salted, 20) != 0)
  99. {
  100. errno = EBADE;
  101. return -1;
  102. }
  103. // 86 characters is the length of the SHA-256 hash
  104. if (write (fd, encrypted + 20, 86) == 86)
  105. return 0;
  106. else
  107. return -1;
  108. }
  109. /*
  110. * _drcSetupNet:
  111. * Do the hard work of establishing a network connection and authenticating
  112. * the password.
  113. *********************************************************************************
  114. */
  115. int _drcSetupNet (const char *ipAddress, const char *port, const char *password)
  116. {
  117. struct addrinfo hints;
  118. struct addrinfo *result, *rp;
  119. struct in6_addr serveraddr;
  120. int remoteFd;
  121. // Start by seeing if we've been given a (textual) numeric IP address
  122. // which will save lookups in getaddrinfo()
  123. memset (&hints, 0, sizeof (hints));
  124. hints.ai_flags = AI_NUMERICSERV;
  125. hints.ai_family = AF_UNSPEC;
  126. hints.ai_socktype = SOCK_STREAM;
  127. hints.ai_protocol = 0;
  128. if (inet_pton (AF_INET, ipAddress, &serveraddr) == 1) // Valid IPv4
  129. {
  130. hints.ai_family = AF_INET;
  131. hints.ai_flags |= AI_NUMERICHOST;
  132. }
  133. else
  134. {
  135. if (inet_pton (AF_INET6, ipAddress, &serveraddr) == 1) // Valid IPv6
  136. {
  137. hints.ai_family = AF_INET6;
  138. hints.ai_flags |= AI_NUMERICHOST;
  139. }
  140. }
  141. // Now use getaddrinfo() with the newly supplied hints
  142. if (getaddrinfo (ipAddress, port, &hints, &result) != 0)
  143. return -1;
  144. // Now try each address in-turn until we get one that connects...
  145. for (rp = result; rp != NULL; rp = rp->ai_next)
  146. {
  147. if ((remoteFd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
  148. continue;
  149. if (connect (remoteFd, rp->ai_addr, rp->ai_addrlen) < 0)
  150. continue;
  151. if (authenticate (remoteFd, password) < 0)
  152. {
  153. close (remoteFd);
  154. errno = EACCES; // Permission denied
  155. return -1;
  156. }
  157. else
  158. return remoteFd;
  159. }
  160. errno = EHOSTUNREACH; // Host unreachable - may not be right, but good enough
  161. return -1; // Nothing connected
  162. }
  163. /*
  164. * myPinMode:
  165. * Change the pin mode on the remote DRC device
  166. *********************************************************************************
  167. */
  168. static void myPinMode (struct wiringPiNodeStruct *node, int pin, int mode)
  169. {
  170. struct drcNetComStruct cmd;
  171. cmd.pin = pin - node->pinBase;
  172. cmd.cmd = DRCN_PIN_MODE;
  173. cmd.data = mode;
  174. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  175. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  176. }
  177. /*
  178. * myPullUpDnControl:
  179. *********************************************************************************
  180. */
  181. static void myPullUpDnControl (struct wiringPiNodeStruct *node, int pin, int mode)
  182. {
  183. struct drcNetComStruct cmd;
  184. cmd.pin = pin - node->pinBase;
  185. cmd.cmd = DRCN_PULL_UP_DN;
  186. cmd.data = mode;
  187. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  188. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  189. }
  190. /*
  191. * myDigitalWrite:
  192. *********************************************************************************
  193. */
  194. static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int value)
  195. {
  196. struct drcNetComStruct cmd;
  197. cmd.pin = pin - node->pinBase;
  198. cmd.cmd = DRCN_DIGITAL_WRITE;
  199. cmd.data = value;
  200. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  201. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  202. }
  203. /*
  204. * myAnalogWrite:
  205. *********************************************************************************
  206. */
  207. static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value)
  208. {
  209. struct drcNetComStruct cmd;
  210. cmd.pin = pin - node->pinBase;
  211. cmd.cmd = DRCN_ANALOG_WRITE;
  212. cmd.data = value;
  213. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  214. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  215. }
  216. /*
  217. * myPwmWrite:
  218. *********************************************************************************
  219. */
  220. static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value)
  221. {
  222. struct drcNetComStruct cmd;
  223. cmd.pin = pin - node->pinBase;
  224. cmd.cmd = DRCN_PWM_WRITE;
  225. cmd.data = value;
  226. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  227. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  228. }
  229. /*
  230. * myAnalogRead:
  231. *********************************************************************************
  232. */
  233. static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
  234. {
  235. struct drcNetComStruct cmd;
  236. cmd.pin = pin - node->pinBase;
  237. cmd.cmd = DRCN_ANALOG_READ;
  238. cmd.data = 0;
  239. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  240. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  241. return cmd.data;
  242. }
  243. /*
  244. * myDigitalRead:
  245. *********************************************************************************
  246. */
  247. static int myDigitalRead (struct wiringPiNodeStruct *node, int pin)
  248. {
  249. struct drcNetComStruct cmd;
  250. cmd.pin = pin - node->pinBase;
  251. cmd.cmd = DRCN_DIGITAL_READ;
  252. cmd.data = 0;
  253. (void)send (node->fd, &cmd, sizeof (cmd), 0);
  254. (void)recv (node->fd, &cmd, sizeof (cmd), 0);
  255. return cmd.data;
  256. }
  257. /*
  258. * drcNet:
  259. * Create a new instance of an DRC GPIO interface.
  260. * Could be a variable nunber of pins here - we might not know in advance.
  261. *********************************************************************************
  262. */
  263. int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password)
  264. {
  265. int fd, len;
  266. struct wiringPiNodeStruct *node;
  267. if ((fd = _drcSetupNet (ipAddress, port, password)) < 0)
  268. return FALSE;
  269. len = sizeof (struct drcNetComStruct);
  270. if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0)
  271. return FALSE;
  272. node = wiringPiNewNode (pinBase, numPins);
  273. node->fd = fd;
  274. node->pinMode = myPinMode;
  275. node->pullUpDnControl = myPullUpDnControl;
  276. node->analogRead = myAnalogRead;
  277. node->analogWrite = myAnalogWrite;
  278. node->digitalRead = myDigitalRead;
  279. node->digitalWrite = myDigitalWrite;
  280. node->pwmWrite = myPwmWrite;
  281. return TRUE;
  282. }