drcNet.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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://projects.drogon.net/raspberry-pi/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] ;
  91. if ((challenge = getChallenge (fd)) == NULL)
  92. return -1 ;
  93. sprintf (salted, "$6$%s$", challenge) ;
  94. encrypted = crypt (pass, salted) ;
  95. // This is an assertion, or sanity check on my part...
  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. * myDigitalWrite8:
  205. *********************************************************************************
  206. static void myDigitalWrite8 (struct wiringPiNodeStruct *node, int pin, int value)
  207. {
  208. struct drcNetComStruct cmd ;
  209. cmd.pin = pin - node->pinBase ;
  210. cmd.cmd = DRCN_DIGITAL_WRITE8 ;
  211. cmd.data = value ;
  212. (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
  213. (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
  214. }
  215. */
  216. /*
  217. * myAnalogWrite:
  218. *********************************************************************************
  219. */
  220. static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value)
  221. {
  222. struct drcNetComStruct cmd ;
  223. cmd.pin = pin - node->pinBase ;
  224. cmd.cmd = DRCN_ANALOG_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. * myPwmWrite:
  231. *********************************************************************************
  232. */
  233. static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value)
  234. {
  235. struct drcNetComStruct cmd ;
  236. cmd.pin = pin - node->pinBase ;
  237. cmd.cmd = DRCN_PWM_WRITE ;
  238. cmd.data = value ;
  239. (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
  240. (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
  241. }
  242. /*
  243. * myAnalogRead:
  244. *********************************************************************************
  245. */
  246. static int myAnalogRead (struct wiringPiNodeStruct *node, int pin)
  247. {
  248. struct drcNetComStruct cmd ;
  249. cmd.pin = pin - node->pinBase ;
  250. cmd.cmd = DRCN_ANALOG_READ ;
  251. cmd.data = 0 ;
  252. (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
  253. (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
  254. return cmd.data ;
  255. }
  256. /*
  257. * myDigitalRead:
  258. *********************************************************************************
  259. */
  260. static int myDigitalRead (struct wiringPiNodeStruct *node, int pin)
  261. {
  262. struct drcNetComStruct cmd ;
  263. cmd.pin = pin - node->pinBase ;
  264. cmd.cmd = DRCN_DIGITAL_READ ;
  265. cmd.data = 0 ;
  266. (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
  267. (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
  268. return cmd.data ;
  269. }
  270. /*
  271. * myDigitalRead8:
  272. *********************************************************************************
  273. static unsigned int myDigitalRead8 (struct wiringPiNodeStruct *node, int pin)
  274. {
  275. struct drcNetComStruct cmd ;
  276. cmd.pin = pin - node->pinBase ;
  277. cmd.cmd = DRCN_DIGITAL_READ8 ;
  278. cmd.data = 0 ;
  279. (void)send (node->fd, &cmd, sizeof (cmd), 0) ;
  280. (void)recv (node->fd, &cmd, sizeof (cmd), 0) ;
  281. return cmd.data ;
  282. }
  283. */
  284. /*
  285. * drcNet:
  286. * Create a new instance of an DRC GPIO interface.
  287. * Could be a variable nunber of pins here - we might not know in advance.
  288. *********************************************************************************
  289. */
  290. int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password)
  291. {
  292. int fd, len ;
  293. struct wiringPiNodeStruct *node ;
  294. if ((fd = _drcSetupNet (ipAddress, port, password)) < 0)
  295. return FALSE ;
  296. len = sizeof (struct drcNetComStruct) ;
  297. if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0)
  298. return FALSE ;
  299. node = wiringPiNewNode (pinBase, numPins) ;
  300. node->fd = fd ;
  301. node->pinMode = myPinMode ;
  302. node->pullUpDnControl = myPullUpDnControl ;
  303. node->analogRead = myAnalogRead ;
  304. node->analogRead = myAnalogRead ;
  305. node->analogWrite = myAnalogWrite ;
  306. node->digitalRead = myDigitalRead ;
  307. node->digitalWrite = myDigitalWrite ;
  308. //node->digitalRead8 = myDigitalRead8 ;
  309. //node->digitalWrite8 = myDigitalWrite8 ;
  310. node->pwmWrite = myPwmWrite ;
  311. return TRUE ;
  312. }