Added mbusio, a client for Modbus TCP
[hexio] / socket.c
1 /* Socket utilities, including parsing and sockaddr juggling.
2  *
3  * From: Rick van Rein <rick@openfortress.nl>
4  */
5
6
7 #include "socket.h"
8
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <limits.h>
12 #include <assert.h>
13 #include <string.h>
14
15 #include <unistd.h>
16 #include <fcntl.h>
17
18
19 #ifdef DEBUG
20 #  include <stdio.h>
21 #  define DPRINTF printf
22 #else
23 #  define DPRINTF(...)
24 #endif
25
26
27 #ifndef PART_OF_KXOVER
28 #include <errno.h>
29 #define kxerrno errno
30 #endif
31
32
33 /* Given a socket address, determine its length.
34  *
35  * This function does not fail.
36  *
37  * TODO:inline
38  */
39 socklen_t sockaddrlen (const struct sockaddr *sa) {
40         assert ((sa->sa_family == AF_INET6) || (sa->sa_family == AF_INET));
41         if (sa->sa_family == AF_INET6) {
42                 return sizeof (struct sockaddr_in6);
43         } else {
44                 return sizeof (struct sockaddr_in );
45         }
46 }
47
48
49 /* Store a raw address from a given family in a socket address,
50  * together with a port that may be set to 0 as a catch-all.
51  */
52 bool socket_address (sa_family_t af, uint8_t *addr, uint16_t portnr, struct sockaddr *sa) {
53         sa->sa_family = af;
54         memset (sa, 0, sockaddrlen (sa));
55         sa->sa_family = af;
56         switch (af) {
57         case AF_INET6:
58                 memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, addr, 16);
59                 ((struct sockaddr_in6 *) sa)->sin6_port = htons (portnr);
60                 return true;
61         case AF_INET:
62 DPRINTF ("DEBUG: socket address (%d.%d.%d.%d, %d)\n", addr [0], addr [1], addr [2], addr [3], portnr);
63                 memcpy (&((struct sockaddr_in  *) sa)->sin_addr,  addr,  4);
64                 ((struct sockaddr_in  *) sa)->sin_port  = htons (portnr);
65                 return true;
66         default:
67                 break;
68         }
69         kxerrno = EINVAL;
70         return false;
71 }
72
73
74 /* Parse an address and port, and store them in a sockaddr of
75  * type AF_INET or AF_INET6.  The space provided is large enough
76  * to hold either, as it is defined as a union.
77  *
78  * The opt_port may be NULL, in which case the port is set to 0
79  * in the returned sockaddr; otherwise, its value is rejected
80  * if it is 0.
81  *
82  * We always try IPv6 address parsing first, but fallback to
83  * IPv4 if we have to, but that fallback is deprecated.  The
84  * port will be syntax-checked and range-checked.
85  *
86  * Return true on success, or false with kxerrno set on error.
87  */
88 bool socket_parse (char *addr, char *opt_port, struct sockaddr *out_sa) {
89         //
90         // Optional port parsing
91         uint16_t portnr = 0;
92         if (opt_port != NULL) {
93                 long p = strtol (opt_port, &opt_port, 10);
94                 if (*opt_port != '\0') {
95                         kxerrno = EINVAL;
96                         return false;
97                 }
98                 if ((p == LONG_MIN) || (p == LONG_MAX) || (p <= 0) || (p > 65535)) {
99                         /* errno is ERANGE */
100                         kxerrno = errno;
101                         return false;
102                 }
103                 portnr = (uint16_t) p;
104         }
105         //
106         // IPv6 address parsing
107         uint8_t raw_addr [16];
108         switch (inet_pton (AF_INET6, addr, raw_addr)) {
109         case 1:
110                 return socket_address (AF_INET6, raw_addr, portnr, out_sa);
111         case 0:
112                 break;
113         default:
114                 break;
115         }
116         //
117         // IPv4 address parsing
118         switch (inet_pton (AF_INET,  addr, raw_addr)) {
119         case 1:
120                 return socket_address (AF_INET,  raw_addr, portnr, out_sa);
121         case 0:
122                 break;
123         default:
124                 break;
125         }
126         //
127         // Report EINVAL as an error condition
128         kxerrno = EINVAL;
129         return false;
130 }
131
132
133 /* Open a connection as a client, to the given address.  Do not bind locally.
134  *
135  * Set contype to one SOCK_DGRAM, SOCK_STREAM or SOCK_SEQPACKET.
136  *
137  * The resulting socket is written to out_sox.
138  *
139  * Return true on success, or false with kxerrno set on failure.
140  * On error, *out_sox is set to -1.
141  */
142 bool socket_client (const struct sockaddr *peer, int contype, int *out_sox) {
143         int sox = -1;
144         sox = socket (peer->sa_family, contype, 0);
145         if (sox < 0) {
146                 goto fail;
147         }
148         if (connect (sox, peer, sockaddrlen (peer)) != 0) {
149                 goto fail;
150         }
151 #ifdef PART_OF_KXOVER
152         int soxflags = fcntl (sox, F_GETFL, 0);
153         if (fcntl (sox, F_SETFL, soxflags | O_NONBLOCK) != 0) {
154                 goto fail;
155         }
156 #endif
157         *out_sox = sox;
158         return true;
159 fail:
160         *out_sox = -1;
161         if (sox >= 0) {
162                 close (sox);
163         }
164         return false;
165 }
166
167
168 /* Open a listening socket as a server, at the given address.
169  *
170  * Set contype to one of SOCK_DGRAM, SOCK_STREAM or SOCK_SEQPACKET.
171  *
172  * The resulting socket is written to out_sox.
173  *
174  * Return true on success, or false with kxerrno set on failure.
175  * On error, *out_sox is set to -1.
176  */
177 bool socket_server (const struct sockaddr *mine, int contype, int *out_sox) {
178         int sox = -1;
179         sox = socket (mine->sa_family, contype, 0);
180         if (sox < 0) {
181                 goto fail;
182         }
183         if (bind (sox, mine, sockaddrlen (mine)) != 0) {
184                 goto fail;
185         }
186         if ((contype == SOCK_STREAM) || (contype == SOCK_SEQPACKET)) {
187                 if (listen (sox, 10) != 0) {
188                         goto fail;
189                 }
190         }
191 #ifdef PART_OF_KXOVER
192         int soxflags = fcntl (sox, F_GETFL, 0);
193         if (fcntl (sox, F_SETFL, soxflags | O_NONBLOCK) != 0) {
194                 goto fail;
195         }
196 #endif
197         *out_sox = sox;
198         return true;
199 fail:
200         *out_sox = -1;
201         if (sox >= 0) {
202                 close (sox);
203         }
204         return false;
205 }
206
207