3.6 基于UDP套接字的网络扫描
这一节将详细分析一个实用程序,使用UDP协议和BSD套接字API,实现简单网络管理协议(Simple Network Management Protocol,SNMP)社区名(community name)扫描。SNMP可用于获取和设置互连计算机和设备上各种类型的管理数据,具有广泛的支持。管理数据的获取和配置通过向远程主机发送封装在UDP数据报中的SNMP GetRequest或SetRequest请求报文来实现。
对于SNMP GetRequest请求,源主机将请求报文发送到远程主机,远程主机接收和验证请求,并将包含应答信息的GetResponse报文封装在一个UDP数据报中发回请求方。对于SNMP SetRequeset请求,源主机将请求报文发送到远程主机,远程主机接收和验证请求,并修改相应的配置。
要获取或修改的信息在SNMP GetRequest或SetRequest报文体中给出。可通过SNMP获取或修改的信息包括远程主机的主机名、IP地址配置以及统计信息。处理SNMP请求的软件称为SNMP代理。SNMP代理软件在161号UDP端口上运行,并监听GetRequest和SetRequest请求。SNMP代理要求收到的请求报文必须包含一个社区名且能与它已知的社区名相匹配,在SNMP请求中社区名在一定程度上起到了口令的作用,如果请求报文提供的社区名不合法,SNMP代理将忽略该请求报文。
好在大部分的SNMP代理软件在默认情况下都有一个名为public的社区名,这对于发现和枚举大量支持SNMP的设备十分有用。例3-9说明了怎样在一个完整的程序中使用UDP套接字发送和接收SNMP GetRequest请求,以获取远程主机的主机名。
例3-9 SNMP扫描器(snmp1.c)
1 /*
2 * snmp1.c
3 *
4 * snmp scanner example program #1.
5 *
6 * foster <jamescfoster@gmail.com>
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <ctype.h>
14
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18
19 #define SNMP1_DEF_PORT 161
20 #define SNMP1_DEF_COMN "public"
21
22 #define SNMP1_BUF_SIZE 0x0400
23
24 /*
25 * hexdisp()
26 *
27 *
28 */
29 void hexdisp (char *buf, int len)
30 {
31 char tmp[16];
32 int x = 0;
33 int y = 0;
34
35 printf("\n");
36
37 for(x=0; x < len; ++x)
38 {
39 tmp[x % 16] = buf[x];
40
41 if((x + 1) % 16 == 0)
42 {
43 for(y=0; y < 16; ++y)
44 {
45 printf("%02X ", tmp[y] & 0xFF);
46 }
47
48 for(y=0; y < 16; ++y)
49 {
50 printf("%c", isprint(tmp[y]) ?
51 tmp[y] : '.');
52 }
53 printf("\n");
54 }
55 }
56
57 if((x % 16) != 0)
58 {
59 for(y=0; y < (x % 16); ++y)
60 {
61 printf("%02X ", tmp[y] & 0xFF);
62 }
63
64 for(y=(x % 16); y < 16 ; ++y)
65 {
66 printf(" ");
67 }
68
69 for(y=0; y < (x % 16); ++y)
70 {
71 printf("%c", isprint(tmp[y]) ? tmp[y] : '.');
72 }
73 }
74
75 printf("\n");
76 }
77
78 /*
79 * makegetreq()
80 *
81 *
82 */
83
84 #define SNMP1_PDU_HEAD "\x30\x00\x02\x01\x00\x04"
85 #define SNMP1_PDU_TAIL "\xa0\x1c\x02\x04\x7e\x16\xa2\x5e" \
86 "\x02\x01\x00\x02\x01\x00\x30\x0e" \
87 "\x30\x0c\x06\x08\x2b\x06\x01\x02" \
88 "\x01\x01\x05\x00\x05\x00"
89
90 int makegetreq (char *buf, int blen, int *olen, char *comn)
91 {
92 int hlen = sizeof(SNMP1_PDU_HEAD) - 1;
93 int tlen = sizeof(SNMP1_PDU_TAIL) - 1;
94 int clen = strlen(comn);
95 int len = 0;
96
97 len = hlen + 1 + clen + tlen;
98 if(len > blen)
99 {
100 printf("insufficient buffer space (%d,%d).\n",
101 blen, len);
102 return(-1);
103 }
104
105 memset(buf, 0x00, blen);
106 memcpy(buf, SNMP1_PDU_HEAD, hlen);
107 memcpy(buf + hlen + 1, comn, clen);
108 memcpy(buf + hlen + 1 + clen, SNMP1_PDU_TAIL, tlen);
109
110 buf[0x01] = 0x23 + clen;
111 buf[hlen] = (char) clen;
112
113 *olen = len;
114
115 return(0);
116 }
117
118 /*
119 * dores()
120 *
121 *
122 */
123 int dores (int sock)
124 {
125 char buf[SNMP1_BUF_SIZE];
126 int ret = 0;
127
128 ret = recvfrom(sock, buf, SNMP1_BUF_SIZE, 0, NULL, NULL);
129 if(ret < 0)
130 {
131 printf("recv() failed.\n");
132 return(-1);
133 }
134
135 hexdisp(buf, ret);
136
137 return(0);
138 }
139
140 /*
141 * doreq()
142 *
143 *
144 */
145 int doreq (int sock, char *comn)
146 {
147 char buf[SNMP1_BUF_SIZE];
148 int len = 0;
149 int ret = 0;
150
151 ret = makegetreq(buf, SNMP1_BUF_SIZE, &len, comn);
152 if(ret < 0)
153 {
154 printf("makegetreq() failed.\n");
155 return(-1);
156 }
157
158 hexdisp(buf, len);
159
160 ret = send(sock, buf, len, 0);
161 if(ret != len)
162 {
163 printf("send() failed.\n");
164 return(-1);
165 }
166
167 return(0);
168 }
169
170 /*
171 * makeudpsock()
172 *
173 *
174 */
175 int makeudpsock (char *targ, unsigned short port)
176 {
177 struct sockaddr_in sin;
178 unsigned int taddr = 0;
179 int sock = 0;
180 int ret = 0;
181
182 taddr = inet_addr(targ);
183 if(taddr == INADDR_NONE)
184 {
185 printf("inet_addr() failed.\n");
186 return(-1);
187 }
188
189 sock = socket(AF_INET, SOCK_DGRAM, 0);
190 if(sock < 0)
191 {
192 printf("socket() failed.\n");
193 return(-1);
194 }
195
196 memset(&sin, 0x0, sizeof(sin));
197
198 sin.sin_family = AF_INET;
199 sin.sin_port = htons(port);
200 sin.sin_addr.s_addr = taddr;
201
202 ret = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
203 if(ret < 0)
204 {
205 printf("connect() failed.\n");
206 return(-1);
207 }
208
209 return(sock);
210 }
211
212 /*
213 * scan()
214 *
215 *
216 */
217 int scan (char *targ, unsigned short port, char *cname)
218 {
219 int sock = 0;
220 int ret = 0;
221
222 sock = makeudpsock(targ, port);
223 if(sock < 0)
224 {
225 printf("makeudpsocket() failed.\n");
226 return(-1);
227 }
228
229 ret = doreq(sock, cname);
230 if(ret < 0)
231 {
232 printf("doreq() failed.\n");
233 return(-1);
234 }
235
236 ret = dores(sock);
237 if(ret < 0)
238 {
239 printf("dores() failed.\n");
240 return(-1);
241 }
242
243 return(0);
244 }
245
246 /*
247 * usage()
248 *
249 *
250 */
251 void usage(char *prog)
252 {
253 printf("snmp1 00.00.01\r\n");
254 printf("usage : %s -t target_ip <-p target_port> " \
255 " <-c community_name>\n", prog);
256 printf("example: %s -t 127.0.0.1 -p 161 -c public\n\n",
257 prog);
258 }
259
260 int
261 main(int argc, char *argv[])
262 {
263 unsigned short port = SNMP1_DEF_PORT;
264 char *targ = NULL;
265 char *comn = SNMP1_DEF_COMN;
266 char ch = 0;
267 int ret = 0;
268
269 opterr = 0;
270 while((ch = getopt(argc, argv, "t:p:c:")) != -1)
271 {
272 switch(ch)
273 {
274 case 't':
275
276 targ = optarg;
277 break;
278
279 case 'p':
280
281 port = atoi(optarg);
282 break;
283
284 case 'c':
285
286 comn = optarg;
287 break;
288
289 case '?':
290 default:
291
292 usage(argv[0]);
293 return(1);
294 }
295 }
296
297 if(targ == NULL)
298 {
299 usage(argv[0]);
300 return(1);
301 }
302
303 printf("using: target: %s; port: %d; " \
304 community name: \"%s\"\n", targ, port, comn);
305
306 ret = scan(targ, port, comn);
307 if(ret < 0)
308 {
309 printf("scan() failed.\n");
310 return(1);
311 }
312
313 printf("scan complete.\n");
314
315 return(0);
316 }






