1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  *
5  * Licensed under the Academic Free License version 2.1
6  */
7 
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 
13 #include <glib.h>
14 
15 #include <libhal.h>
16 #include <logger.h>
17 
18 #undef PACKAGE_STRING
19 #undef PACKAGE_VERSION
20 
21 #include <net-snmp/net-snmp-config.h>
22 #include <net-snmp/net-snmp-includes.h>
23 
24 #include "network-discovery.h"
25 #include "printer.h"
26 
27 #define	NP(x)	(x?x:"NULL")
28 
29 static GList *new_addrs = NULL;
30 
31 static void
add_snmp_device(LibHalContext * ctx,char * parent,char * name,char * community)32 add_snmp_device(LibHalContext *ctx, char *parent, char *name, char *community)
33 {
34 	/* most printers listen on the appsocket port (9100) */
35 	if (is_listening(name, 9100) == 0) {
36 		char device[128];
37 
38 		snprintf(device, sizeof (device), "socket://%s:9100", name);
39 
40 		add_network_printer(ctx, parent, name, device, community);
41 	}
42 
43 	/*
44 	 * This would be a good place to detect other types of devices or other
45 	 * device capabilities.  scanners, removable media, storage, ...
46 	 */
47 }
48 
49 static int
snmp_response_cb(int operation,struct snmp_session * sp,int reqid,struct snmp_pdu * pdu,void * data)50 snmp_response_cb(int operation, struct snmp_session *sp, int reqid,
51 		struct snmp_pdu *pdu, void *data)
52 {
53 	struct sockaddr_in *addr = pdu->transport_data;
54 	char *name;
55 
56 	name = inet_ntoa(addr->sin_addr);
57 
58 	/* have we already seen this network device */
59 	if (device_seen(name) == FALSE)
60 		new_addrs = g_list_append(new_addrs, strdup(name));
61 
62 	return (0);
63 }
64 
65 gboolean
scan_for_devices_using_snmp(LibHalContext * ctx,char * parent,char * community,char * network)66 scan_for_devices_using_snmp(LibHalContext *ctx, char *parent, char *community,
67 		char *network)
68 {
69 	struct snmp_session session, *ss;
70 	struct snmp_pdu *request = NULL, *response = NULL;
71 	oid Oid[MAX_OID_LEN];
72 	unsigned int oid_len = MAX_OID_LEN;
73 	GList *elem;
74 
75 	HAL_DEBUG(("scan_for_devices_using_snmp(0x%8.8x, %s, %s, %s)",
76 	    ctx, NP(parent), NP(community), NP(network)));
77 
78 	init_snmp("snmp-scan");
79 	init_mib();
80 
81 	/* initialize the SNMP session */
82 	snmp_sess_init(&session);
83 	session.peername = network;
84 	session.community = (uchar_t *)community;
85 	session.community_len = strlen((const char *)session.community);
86 	session.version = SNMP_VERSION_1;
87 
88 	if ((ss = snmp_open(&session)) == NULL)
89 		return (FALSE);
90 
91 	/* initialize the request PDU */
92 	request = snmp_pdu_create(SNMP_MSG_GET);
93 
94 	/* add the requested data (everyone should have a sysDescr.0) */
95 	if (!read_objid("SNMPv2-MIB::sysDescr.0", Oid, &oid_len))
96 		snmp_perror("sysDescr.0");
97 	snmp_add_null_var(request, Oid, oid_len);
98 
99 	snmp_async_send(ss, request, snmp_response_cb, NULL);
100 
101 	/* detect any new devices */
102 	while (1) {
103 		int fds = 0, block = 0;
104 		fd_set fdset;
105 		struct timeval timeout;
106 
107 		FD_ZERO(&fdset);
108 		snmp_select_info(&fds, &fdset, &timeout, &block);
109 		fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);
110 		if (fds < 0) {
111 			perror("select failed");
112 			break;
113 		} if (fds == 0) {
114 			break;
115 		} else {
116 			snmp_read(&fdset);
117 		}
118 	}
119 
120 	snmp_close(ss);
121 
122 	/* add the newly detected devices */
123 	for (elem = new_addrs; elem != NULL; elem = g_list_next(elem)) {
124 		add_snmp_device(ctx, parent, (char *)elem->data, community);
125 		free(elem->data);
126 	}
127 	g_list_free(new_addrs);
128 	new_addrs = NULL;
129 
130 	return (TRUE);
131 }
132