# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmpTCPIPv6Domain.c
1 #include <net-snmp/net-snmp-config.h>
2
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <errno.h>
6
7 #if HAVE_STRING_H
8 #include <string.h>
9 #else
10 #include <strings.h>
11 #endif
12 #if HAVE_STDLIB_H
13 #include <stdlib.h>
14 #endif
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #if HAVE_SYS_SOCKET_H
19 #include <sys/socket.h>
20 #endif
21 #if HAVE_NETINET_IN_H
22 #include <netinet/in.h>
23 #endif
24 #if HAVE_ARPA_INET_H
25 #include <arpa/inet.h>
26 #endif
27 #if HAVE_NETDB_H
28 #include <netdb.h>
29 #endif
30 #if HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 #if HAVE_DMALLOC_H
35 #include <dmalloc.h>
36 #endif
37
38 #include <net-snmp/types.h>
39 #include <net-snmp/output_api.h>
40 #include <net-snmp/config_api.h>
41
42 #include <net-snmp/library/snmp_transport.h>
43 #include <net-snmp/library/snmpUDPIPv6Domain.h>
44 #include <net-snmp/library/snmpTCPIPv6Domain.h>
45
46 oid netsnmp_TCPIPv6Domain[] = { ENTERPRISE_MIB, 3, 3, 5 };
47 static netsnmp_tdomain tcp6Domain;
48
49 /*
50  * Return a string representing the address in data, or else the "far end"
51  * address if data is NULL.  
52  */
53
54 static char *
55 netsnmp_tcp6_fmtaddr(netsnmp_transport *t, void *data, int len)
56 {
57     struct sockaddr_in6 *to = NULL;
58
59     DEBUGMSGTL(("netsnmp_tcp6", "fmtaddr: t = %p, data = %p, len = %d\n", t,
60                 data, len));
61     if (data != NULL && len == sizeof(struct sockaddr_in6)) {
62         to = (struct sockaddr_in6 *) data;
63     } else if (t != NULL && t->data != NULL) {
64         to = (struct sockaddr_in6 *) t->data;
65     }
66     if (to == NULL) {
67         return strdup("TCP/IPv6: unknown");
68     } else {
69         char addr[INET6_ADDRSTRLEN];
70         char tmp[INET6_ADDRSTRLEN + 8];
71
72         sprintf(tmp, "[%s]:%hd",
73                 inet_ntop(AF_INET6, (void *) &(to->sin6_addr), addr,
74                           INET6_ADDRSTRLEN), ntohs(to->sin6_port));
75         return strdup(tmp);
76     }
77 }
78
79 /*
80  * You can write something into opaque that will subsequently get passed back 
81  * to your send function if you like.  For instance, you might want to
82  * remember where a PDU came from, so that you can send a reply there...  
83  */
84
85 static int
86 netsnmp_tcp6_recv(netsnmp_transport *t, void *buf, int size,
87                   void **opaque, int *olength)
88 {
89     int rc = -1;
90
91     if (t != NULL && t->sock >= 0) {
92         while (rc < 0) {
93             rc = recv(t->sock, buf, size, 0);
94             if (rc < 0 && errno != EINTR) {
95                 DEBUGMSGTL(("netsnmp_tcp6", "recv fd %d err %d (\"%s\")\n",
96                             t->sock, errno, strerror(errno)));
97                 return -1;
98             }
99         }
100         DEBUGMSGTL(("netsnmp_tcp6", "recv fd %d got %d bytes\n", t->sock, rc));
101     } else {
102         return -1;
103     }
104
105     if (opaque != NULL && olength != NULL) {
106         if (t->data_length > 0) {
107             if ((*opaque = malloc(t->data_length)) != NULL) {
108                 memcpy(*opaque, t->data, t->data_length);
109                 *olength = t->data_length;
110             } else {
111                 *olength = 0;
112             }
113         } else {
114             *opaque = NULL;
115             *olength = 0;
116         }
117     }
118
119     return rc;
120 }
121
122 static int
123 netsnmp_tcp6_send(netsnmp_transport *t, void *buf, int size,
124                   void **opaque, int *olength)
125 {
126     int rc = -1;
127
128     if (t != NULL && t->sock >= 0) {
129         while (rc < 0) {
130             rc = send(t->sock, buf, size, 0);
131             if (rc < 0 && errno != EINTR) {
132                 break;
133             }
134         }
135     }
136     return rc;
137 }
138
139 static int
140 netsnmp_tcp6_close(netsnmp_transport *t)
141 {
142     int rc = -1;
143     if (t != NULL && t->sock >= 0) {
144         DEBUGMSGTL(("netsnmp_tcp6", "close fd %d\n", t->sock));
145 #ifndef HAVE_CLOSESOCKET
146         rc = close(t->sock);
147 #else
148         rc = closesocket(t->sock);
149 #endif
150         t->sock = -1;
151     }
152     return rc;
153 }
154
155 static int
156 netsnmp_tcp6_accept(netsnmp_transport *t)
157 {
158     struct sockaddr_in6 *farend = NULL;
159     int             newsock = -1, sockflags = 0;
160     socklen_t       farendlen = sizeof(struct sockaddr_in6);
161     char           *string = NULL;
162
163     farend = (struct sockaddr_in6 *) malloc(sizeof(struct sockaddr_in6));
164
165     if (farend == NULL) {
166         /*
167          * Indicate that the acceptance of this socket failed.  
168          */
169         DEBUGMSGTL(("netsnmp_tcp6", "accept: malloc failed\n"));
170         return -1;
171     }
172
173     if (t != NULL && t->sock >= 0) {
174         newsock = accept(t->sock, (struct sockaddr *) farend, &farendlen);
175
176         if (newsock < 0) {
177             DEBUGMSGTL(("netsnmp_tcp6","accept failed rc %d errno %d \"%s\"\n",
178                         newsock, errno, strerror(errno)));
179             free(farend);
180             return newsock;
181         }
182
183         if (t->data != NULL) {
184             free(t->data);
185         }
186
187         t->data = farend;
188         t->data_length = farendlen;
189         string = netsnmp_tcp6_fmtaddr(NULL, farend, farendlen);
190         DEBUGMSGTL(("netsnmp_tcp6", "accept succeeded (from %s)\n", string));
191         free(string);
192
193         /*
194          * Try to make the new socket blocking.  
195          */
196
197 #ifdef WIN32
198         ioctlsocket(newsock, FIONBIO, &sockflags);
199 #else
200         if ((sockflags = fcntl(newsock, F_GETFL, 0)) >= 0) {
201             fcntl(newsock, F_SETFL, (sockflags & ~O_NONBLOCK));
202         } else {
203             DEBUGMSGTL(("netsnmp_tcp6", "accept: couldn't f_getfl of fd %d\n",
204                         newsock));
205         }
206 #endif
207         return newsock;
208     } else {
209         free(farend);
210         return -1;
211     }
212 }
213
214
215
216 /*
217  * Open a TCP/IPv6-based transport for SNMP.  Local is TRUE if addr is the
218  * local address to bind to (i.e. this is a server-type session); otherwise
219  * addr is the remote address to send things to.  
220  */
221
222 netsnmp_transport *
223 netsnmp_tcp6_transport(struct sockaddr_in6 *addr, int local)
224 {
225     netsnmp_transport *t = NULL;
226     int             rc = 0;
227     char           *string = NULL;
228
229     if (addr == NULL || addr->sin6_family != AF_INET6) {
230         return NULL;
231     }
232
233     t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
234     if (t == NULL) {
235         return NULL;
236     }
237     memset(t, 0, sizeof(netsnmp_transport));
238
239     string = netsnmp_tcp6_fmtaddr(NULL, (void *)addr,
240                                   sizeof(struct sockaddr_in6));
241     DEBUGMSGTL(("netsnmp_tcp6", "open %s %s\n", local ? "local" : "remote",
242                 string));
243     free(string);
244
245     memset(t, 0, sizeof(netsnmp_transport));
246
247     t->data = malloc(sizeof(struct sockaddr_in6));
248     if (t->data == NULL) {
249         netsnmp_transport_free(t);
250         return NULL;
251     }
252     t->data_length = sizeof(struct sockaddr_in6);
253     memcpy(t->data, addr, sizeof(struct sockaddr_in6));
254
255     t->domain = netsnmp_TCPIPv6Domain;
256     t->domain_length = sizeof(netsnmp_TCPIPv6Domain) / sizeof(oid);
257
258     t->sock = socket(PF_INET6, SOCK_STREAM, 0);
259     if (t->sock < 0) {
260         netsnmp_transport_free(t);
261         return NULL;
262     }
263
264     t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
265
266     if (local) {
267         int sockflags = 0, opt = 1;
268
269         /*
270          * This session is inteneded as a server, so we must bind on to the
271          * given IP address, which may include an interface address, or could
272          * be INADDR_ANY, but certainly includes a port number.
273          */
274
275         t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
276         t->local = malloc(18);
277         if (t->local == NULL) {
278             netsnmp_tcp6_close(t);
279             netsnmp_transport_free(t);
280             return NULL;
281         }
282         memcpy(t->local, addr->sin6_addr.s6_addr, 16);
283         t->local[16] = (addr->sin6_port & 0xff00) >> 8;
284         t->local[17] = (addr->sin6_port & 0x00ff) >> 0;
285         t->local_length = 18;
286
287         /*
288          * We should set SO_REUSEADDR too.  
289          */
290
291         setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
292
293         rc = bind(t->sock, (struct sockaddr *) addr,
294                   sizeof(struct sockaddr_in6));
295         if (rc != 0) {
296             netsnmp_tcp6_close(t);
297             netsnmp_transport_free(t);
298             return NULL;
299         }
300
301         /*
302          * Since we are going to be letting select() tell us when connections
303          * are ready to be accept()ed, we need to make the socket n0n-blocking
304          * to avoid the race condition described in W. R. Stevens, ``Unix
305          * Network Programming Volume I Second Edition'', pp. 422--4, which
306          * could otherwise wedge the agent.
307          */
308
309 #ifdef WIN32
310         opt = 1;
311         ioctlsocket(t->sock, FIONBIO, &opt);
312 #else
313         sockflags = fcntl(t->sock, F_GETFL, 0);
314         fcntl(t->sock, F_SETFL, sockflags | O_NONBLOCK);
315 #endif
316
317         /*
318          * Now sit here and wait for connections to arrive.  
319          */
320
321         rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN);
322         if (rc != 0) {
323             netsnmp_tcp6_close(t);
324             netsnmp_transport_free(t);
325             return NULL;
326         }
327     } else {
328         t->remote = malloc(18);
329         if (t->remote == NULL) {
330             netsnmp_tcp6_close(t);
331             netsnmp_transport_free(t);
332             return NULL;
333         }
334         memcpy(t->remote, addr->sin6_addr.s6_addr, 16);
335         t->remote[16] = (addr->sin6_port & 0xff00) >> 8;
336         t->remote[17] = (addr->sin6_port & 0x00ff) >> 0;
337         t->remote_length = 18;
338
339         /*
340          * This is a client-type session, so attempt to connect to the far
341          * end.  We don't go non-blocking here because it's not obvious what
342          * you'd then do if you tried to do snmp_sends before the connection
343          * had completed.  So this can block.
344          */
345
346         rc = connect(t->sock, (struct sockaddr *) addr,
347                      sizeof(struct sockaddr_in6));
348
349         DEBUGMSGTL(("netsnmp_tcp6", "connect returns %d\n", rc));
350
351         if (rc < 0) {
352             netsnmp_tcp6_close(t);
353             netsnmp_transport_free(t);
354             return NULL;
355         }
356     }
357
358     /*
359      * Message size is not limited by this transport (hence msgMaxSize
360      * is equal to the maximum legal size of an SNMP message).  
361      */
362
363     t->msgMaxSize = 0x7fffffff;
364     t->f_recv     = netsnmp_tcp6_recv;
365     t->f_send     = netsnmp_tcp6_send;
366     t->f_close    = netsnmp_tcp6_close;
367     t->f_accept   = netsnmp_tcp6_accept;
368     t->f_fmtaddr  = netsnmp_tcp6_fmtaddr;
369
370     return t;
371 }
372
373
374
375 netsnmp_transport *
376 netsnmp_tcp6_create_tstring(const char *string, int local)
377 {
378     struct sockaddr_in6 addr;
379
380     if (netsnmp_sockaddr_in6(&addr, string, 0)) {
381         return netsnmp_tcp6_transport(&addr, local);
382     } else {
383         return NULL;
384     }
385 }
386
387
388 /*
389  * See:
390  * 
391  * http://www.ietf.org/internet-drafts/draft-ietf-ops-taddress-mib-01.txt
392  * 
393  * (or newer equivalent) for details of the TC which we are using for
394  * the mapping here.  
395  */
396
397 netsnmp_transport *
398 netsnmp_tcp6_create_ostring(const u_char * o, size_t o_len, int local)
399 {
400     struct sockaddr_in6 addr;
401
402     if (o_len == 18) {
403         memset((u_char *) & addr, 0, sizeof(struct sockaddr_in6));
404         addr.sin6_family = AF_INET6;
405         memcpy((u_char *) & (addr.sin6_addr.s6_addr), o, 16);
406         addr.sin6_port = (o[16] << 8) + o[17];
407         return netsnmp_tcp6_transport(&addr, local);
408     }
409     return NULL;
410 }
411
412
413 void
414 netsnmp_tcp6_ctor(void)
415 {
416     tcp6Domain.name = netsnmp_TCPIPv6Domain;
417     tcp6Domain.name_length = sizeof(netsnmp_TCPIPv6Domain) / sizeof(oid);
418     tcp6Domain.f_create_from_tstring = netsnmp_tcp6_create_tstring;
419     tcp6Domain.f_create_from_ostring = netsnmp_tcp6_create_ostring;
420     tcp6Domain.prefix = calloc(4, sizeof(char *));
421     tcp6Domain.prefix[0] = "tcp6";
422     tcp6Domain.prefix[1] = "tcpv6";
423     tcp6Domain.prefix[2] = "tcpipv6";
424
425     netsnmp_tdomain_register(&tcp6Domain);
426 }