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