networkbrowser: nbtscan: reduce use of malloc
[enigma2-plugins.git] / networkbrowser / src / lib / nbtscan.c
1 /*###########################################################################
2 #
3 # written by :  Stephen J. Friedl
4 #               Software Consultant
5 #               steve@unixwiz.net
6 #
7 # Copyright (C) 2007 - 2008 by
8 # nixkoenner <nixkoenner@newnigma2.to>
9 # License: GPL
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #
25 ###########################################################################*/
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <stdlib.h>
32 #include <sys/time.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #if HAVE_STDINT_H
37 #include <stdint.h>
38 #endif
39
40 #include "nbtscan.h"
41
42 int quiet = 0;
43
44 static int set_range(const char *range_str, struct ip_range *range_struct)
45 {
46         if (is_ip(range_str, range_struct))
47                 return 1;
48         if (is_range1(range_str, range_struct))
49                 return 1;
50         if (is_range2(range_str, range_struct))
51                 return 1;
52         return 0;
53 };
54
55 static int python_hostinfo(struct in_addr addr, const struct nb_host_info *hostinfo, netinfo *nInfo)
56 {
57         int unique;
58         uint8_t service;
59
60         service = hostinfo->names[0].ascii_name[15];
61         unique = !(hostinfo->names[0].rr_flags & 0x0080);
62         strncpy(nInfo->name, hostinfo->names[0].ascii_name, 15);
63         strncpy(nInfo->domain, hostinfo->names[1].ascii_name, 15);
64         sprintf(nInfo->service, "%s", (char *)getnbservicename(service, unique, hostinfo->names[0].ascii_name));
65         if (hostinfo->footer) {
66                 sprintf(nInfo->mac, "%02x:%02x:%02x:%02x:%02x:%02x",
67                         hostinfo->footer->adapter_address[0], hostinfo->footer->adapter_address[1],
68                         hostinfo->footer->adapter_address[2], hostinfo->footer->adapter_address[3],
69                         hostinfo->footer->adapter_address[4], hostinfo->footer->adapter_address[5]);
70         }
71         strcpy(nInfo->ip, inet_ntoa(addr));
72         return 0;
73 }
74
75 #define BUFFSIZE 1024
76
77 int netInfo(char *pythonIp, netinfo * nInfo)
78 {
79         int timeout = 10000, send_ok;
80         struct ip_range range;
81         char buff[BUFFSIZE];
82         int sock;
83         unsigned int addr_size;
84         struct sockaddr_in src_sockaddr, dest_sockaddr;
85         struct in_addr *prev_in_addr = NULL;
86         struct in_addr next_in_addr;
87         struct timeval select_timeout, last_send_time, current_time, diff_time, send_interval;
88         struct timeval transmit_started, now, recv_time;
89         struct nb_host_info *hostinfo;
90         fd_set fdsr;
91         fd_set fdsw;
92         int size;
93         int pos = 0;
94         struct list *scanned;
95         uint32_t rtt_base;      /* Base time (seconds) for round trip time calculations */
96         float rtt;              /* most recent measured RTT, seconds */
97         float srtt = 0;         /* smoothed rtt estimator, seconds */
98         float rttvar = 0.75;    /* smoothed mean deviation, seconds */
99         double delta;           /* used in retransmit timeout calculations */
100         int rto, retransmits = 0, more_to_send = 1, i;
101         char errmsg[80];
102
103         if (!set_range(pythonIp, &range)) {
104                 printf("Error: %s is not an IP address or address range.\n", pythonIp);
105                 return 0;
106         }
107         /* Finished with options */
108   /*************************/
109
110         /* Prepare socket and address structures */
111   /*****************************************/
112         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
113         if (sock < 0)
114                 err_die("Failed to create socket", quiet);
115
116         bzero((void *)&src_sockaddr, sizeof(src_sockaddr));
117         src_sockaddr.sin_family = AF_INET;
118         if (bind(sock, (struct sockaddr *)&src_sockaddr, sizeof(src_sockaddr)) == -1)
119                 err_die("Failed to bind", quiet);
120
121         FD_ZERO(&fdsr);
122         FD_SET(sock, &fdsr);
123
124         FD_ZERO(&fdsw);
125         FD_SET(sock, &fdsw);
126
127         /* timeout is in milliseconds */
128         select_timeout.tv_sec = 60;     /* Default 1 min to survive ARP timeouts */
129         select_timeout.tv_usec = 0;
130
131         addr_size = sizeof(struct sockaddr_in);
132
133         /* Calculate interval between subsequent sends */
134
135         timerclear(&send_interval);
136         send_interval.tv_usec = 1;      /* for 10baseT interval should be about 1 ms */
137
138         gettimeofday(&last_send_time, NULL);    /* Get current time */
139
140         rtt_base = last_send_time.tv_sec;
141
142         /* Send queries, receive answers and print results */
143   /***************************************************/
144
145         scanned = new_list();
146
147         for (i = 0; i <= retransmits; i++) {
148                 gettimeofday(&transmit_started, NULL);
149                 while ((select(sock + 1, &fdsr, &fdsw, NULL, &select_timeout)) > 0) {
150                         if (FD_ISSET(sock, &fdsr)) {
151                                 if ((size = recvfrom(sock, buff, BUFFSIZE, 0, (struct sockaddr *)&dest_sockaddr, &addr_size)) <= 0) {
152                                         snprintf(errmsg, 80, "%s\tRecvfrom failed", inet_ntoa(dest_sockaddr.sin_addr));
153                                         err_print(errmsg, quiet);
154                                         continue;
155                                 };
156                                 gettimeofday(&recv_time, NULL);
157                                 memset(&hostinfo, 0, sizeof(hostinfo));
158                                 hostinfo = (struct nb_host_info *)parse_response(buff, size);
159                                 if (!hostinfo) {
160                                         err_print("parse_response returned NULL", quiet);
161                                         continue;
162                                 };
163                                 /* If this packet isn't a duplicate */
164                                 if (insert(scanned, ntohl(dest_sockaddr.sin_addr.s_addr))) {
165                                         rtt = recv_time.tv_sec + recv_time.tv_usec / 1000000 - rtt_base - hostinfo->header->transaction_id / 1000;
166                                         /* Using algorithm described in Stevens' 
167                                            Unix Network Programming */
168                                         delta = rtt - srtt;
169                                         srtt += delta / 8;
170                                         if (delta < 0.0)
171                                                 delta = -delta;
172                                         rttvar += (delta - rttvar) / 4;
173                                         if (hostinfo->names == 0x0) {
174                                                 printf("hostinfo->names == NULL\n");
175                                         } else {
176                                                 python_hostinfo(dest_sockaddr.sin_addr, hostinfo, &nInfo[pos++]);
177                                         }
178                                 };
179                                 free(hostinfo);
180                         };
181
182                         FD_ZERO(&fdsr);
183                         FD_SET(sock, &fdsr);
184
185                         /* check if send_interval time passed since last send */
186                         gettimeofday(&current_time, NULL);
187                         timersub(&current_time, &last_send_time, &diff_time);
188                         send_ok = timercmp(&diff_time, &send_interval, >=);
189
190                         if (more_to_send && FD_ISSET(sock, &fdsw) && send_ok) {
191                                 if (next_address(&range, prev_in_addr, &next_in_addr)) {
192                                         if (!in_list(scanned, ntohl(next_in_addr.s_addr)))
193                                                 send_query(sock, next_in_addr, rtt_base);
194                                         prev_in_addr = &next_in_addr;
195                                         /* Update last send time */
196                                         gettimeofday(&last_send_time, NULL);
197                                 } else {        /* No more queries to send */
198                                         more_to_send = 0;
199                                         FD_ZERO(&fdsw);
200                                         /* timeout is in milliseconds */
201                                         select_timeout.tv_sec = timeout / 1000;
202                                         select_timeout.tv_usec = (timeout % 1000) * 1000;       /* Microseconds */
203                                         continue;
204                                 };
205                         };
206                         if (more_to_send) {
207                                 FD_ZERO(&fdsw);
208                                 FD_SET(sock, &fdsw);
209                         };
210                 };
211
212                 if (i >= retransmits)
213                         break;  /* If we are not going to retransmit
214                                    we can finish right now without waiting */
215
216                 rto = (srtt + 4 * rttvar) * (i + 1);
217
218                 if (rto < 2.0)
219                         rto = 2.0;
220                 if (rto > 60.0)
221                         rto = 60.0;
222                 gettimeofday(&now, NULL);
223
224                 if (now.tv_sec < (transmit_started.tv_sec + rto))
225                         sleep((transmit_started.tv_sec + rto) - now.tv_sec);
226                 prev_in_addr = NULL;
227                 more_to_send = 1;
228                 FD_ZERO(&fdsw);
229                 FD_SET(sock, &fdsw);
230                 FD_ZERO(&fdsr);
231                 FD_SET(sock, &fdsr);
232         };
233
234         delete_list(scanned);
235         return 0;
236 };