xref: /haiku/src/servers/net/NetServer.cpp (revision ba499cdc3336fb89429027418871bf263f1f5e14)
1 /*
2  * Copyright 2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "AutoconfigLooper.h"
11 #include "NetServer.h"
12 #include "Services.h"
13 #include "Settings.h"
14 
15 #include <Alert.h>
16 #include <Application.h>
17 #include <Directory.h>
18 #include <Entry.h>
19 #include <NodeMonitor.h>
20 #include <Path.h>
21 #include <TextView.h>
22 
23 #include <arpa/inet.h>
24 #include <net/if_dl.h>
25 #include <net/if_types.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <sys/sockio.h>
29 
30 #include <map>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string>
35 #include <string.h>
36 #include <unistd.h>
37 
38 
39 typedef std::map<std::string, BLooper*> LooperMap;
40 
41 
42 class NetServer : public BApplication {
43 	public:
44 		NetServer();
45 
46 		virtual void AboutRequested();
47 		virtual void ReadyToRun();
48 		virtual void MessageReceived(BMessage* message);
49 
50 	private:
51 		bool _TestForInterface(int socket, const char* name);
52 		status_t _ConfigureInterface(int socket, BMessage& interface,
53 			bool fromMessage = false);
54 		bool _QuitLooperForDevice(const char* device);
55 		BLooper* _LooperForDevice(const char* device);
56 		status_t _ConfigureDevice(int socket, const char* path);
57 		void _ConfigureDevices(int socket, const char* path);
58 		void _ConfigureInterfaces(int socket);
59 		void _BringUpInterfaces();
60 		void _StartServices();
61 
62 		Settings	fSettings;
63 		LooperMap	fDeviceMap;
64 		BMessenger	fServices;
65 };
66 
67 
68 struct address_family {
69 	int			family;
70 	const char*	name;
71 	const char*	identifiers[4];
72 	bool		(*parse_address)(const char* string, sockaddr* _address);
73 	void		(*set_any_address)(sockaddr* address);
74 	void		(*set_port)(sockaddr* address, int32 port);
75 };
76 
77 // AF_INET family
78 static bool inet_parse_address(const char* string, sockaddr* address);
79 static void inet_set_any_address(sockaddr* address);
80 static void inet_set_port(sockaddr* address, int32 port);
81 
82 static const address_family kFamilies[] = {
83 	{
84 		AF_INET,
85 		"inet",
86 		{"AF_INET", "inet", "ipv4", NULL},
87 		inet_parse_address,
88 		inet_set_any_address,
89 		inet_set_port
90 	},
91 	{ -1, NULL, {NULL}, NULL }
92 };
93 
94 
95 static bool
96 inet_parse_address(const char* string, sockaddr* _address)
97 {
98 	in_addr inetAddress;
99 
100 	if (inet_aton(string, &inetAddress) != 1)
101 		return false;
102 
103 	sockaddr_in& address = *(sockaddr_in *)_address;
104 	address.sin_family = AF_INET;
105 	address.sin_len = sizeof(struct sockaddr_in);
106 	address.sin_port = 0;
107 	address.sin_addr = inetAddress;
108 	memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
109 
110 	return true;
111 }
112 
113 
114 void
115 inet_set_any_address(sockaddr* _address)
116 {
117 	sockaddr_in& address = *(sockaddr_in*)_address;
118 	address.sin_family = AF_INET;
119 	address.sin_len = sizeof(struct sockaddr_in);
120 	address.sin_port = 0;
121 	address.sin_addr.s_addr = INADDR_ANY;
122 	memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
123 }
124 
125 
126 void
127 inet_set_port(sockaddr* _address, int32 port)
128 {
129 	sockaddr_in& address = *(sockaddr_in*)_address;
130 	address.sin_port = port;
131 }
132 
133 
134 //	#pragma mark -
135 
136 
137 bool
138 get_family_index(const char* name, int32& familyIndex)
139 {
140 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
141 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
142 			if (!strcmp(name, kFamilies[i].identifiers[j])) {
143 				// found a match
144 				familyIndex = i;
145 				return true;
146 			}
147 		}
148 	}
149 
150 	// defaults to AF_INET
151 	familyIndex = 0;
152 	return false;
153 }
154 
155 
156 int
157 family_at_index(int32 index)
158 {
159 	return kFamilies[index].family;
160 }
161 
162 
163 bool
164 parse_address(int32 familyIndex, const char* argument, struct sockaddr& address)
165 {
166 	if (argument == NULL)
167 		return false;
168 
169 	return kFamilies[familyIndex].parse_address(argument, &address);
170 }
171 
172 
173 void
174 set_any_address(int32 familyIndex, struct sockaddr& address)
175 {
176 	kFamilies[familyIndex].set_any_address(&address);
177 }
178 
179 
180 void
181 set_port(int32 familyIndex, struct sockaddr& address, int32 port)
182 {
183 	kFamilies[familyIndex].set_port(&address, port);
184 }
185 
186 
187 bool
188 prepare_request(ifreq& request, const char* name)
189 {
190 	if (strlen(name) > IF_NAMESIZE)
191 		return false;
192 
193 	strcpy(request.ifr_name, name);
194 	return true;
195 }
196 
197 
198 status_t
199 get_mac_address(const char* device, uint8* address)
200 {
201 	int socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
202 	if (socket < 0)
203 		return errno;
204 
205 	ifreq request;
206 	if (!prepare_request(request, device)) {
207 		close(socket);
208 		return B_ERROR;
209 	}
210 
211 	if (ioctl(socket, SIOCGIFADDR, &request, sizeof(struct ifreq)) < 0) {
212 		close(socket);
213 		return errno;
214 	}
215 
216 	close(socket);
217 
218 	sockaddr_dl &link = *(sockaddr_dl *)&request.ifr_addr;
219 	if (link.sdl_type != IFT_ETHER)
220 		return B_BAD_TYPE;
221 
222 	if (link.sdl_alen == 0)
223 		return B_ENTRY_NOT_FOUND;
224 
225 	uint8 *mac = (uint8 *)LLADDR(&link);
226 	memcpy(address, mac, 6);
227 
228 	return B_OK;
229 }
230 
231 
232 //	#pragma mark -
233 
234 
235 NetServer::NetServer()
236 	: BApplication("application/x-vnd.haiku-net_server")
237 {
238 }
239 
240 
241 void
242 NetServer::AboutRequested()
243 {
244 	BAlert *alert = new BAlert("about", "Networking Server\n"
245 		"\tCopyright " B_UTF8_COPYRIGHT "2006, Haiku.\n", "Ok");
246 	BTextView *view = alert->TextView();
247 	BFont font;
248 
249 	view->SetStylable(true);
250 
251 	view->GetFont(&font);
252 	font.SetSize(18);
253 	font.SetFace(B_BOLD_FACE);
254 	view->SetFontAndColor(0, 17, &font);
255 
256 	alert->Go(NULL);
257 }
258 
259 
260 void
261 NetServer::ReadyToRun()
262 {
263 	fSettings.StartMonitoring(this);
264 	_BringUpInterfaces();
265 	_StartServices();
266 }
267 
268 
269 void
270 NetServer::MessageReceived(BMessage* message)
271 {
272 	switch (message->what) {
273 		case B_NODE_MONITOR:
274 			fSettings.Update(message);
275 			break;
276 
277 		case kMsgInterfaceSettingsUpdated:
278 		{
279 			// we need a socket to talk to the networking stack
280 			int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
281 			if (socket < 0)
282 				break;
283 
284 			_ConfigureInterfaces(socket);
285 			close(socket);
286 			break;
287 		}
288 
289 		case kMsgServiceSettingsUpdated:
290 		{
291 			BMessage update = fSettings.Services();
292 			update.what = kMsgUpdateServices;
293 
294 			fServices.SendMessage(&update);
295 			break;
296 		}
297 
298 		case kMsgConfigureInterface:
299 		{
300 			if (!message->ReturnAddress().IsTargetLocal()) {
301 				// for now, we only accept this message from add-ons
302 				break;
303 			}
304 
305 			// we need a socket to talk to the networking stack
306 			int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
307 			if (socket < 0)
308 				break;
309 
310 			status_t status = _ConfigureInterface(socket, *message, true);
311 
312 			BMessage reply(B_REPLY);
313 			reply.AddInt32("status", status);
314 			message->SendReply(&reply);
315 
316 			close(socket);
317 			break;
318 		}
319 
320 		default:
321 			BApplication::MessageReceived(message);
322 			return;
323 	}
324 }
325 
326 
327 bool
328 NetServer::_TestForInterface(int socket, const char* name)
329 {
330 	// get a list of all interfaces
331 
332 	ifconf config;
333 	config.ifc_len = sizeof(config.ifc_value);
334 	if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
335 		return false;
336 
337 	uint32 count = (uint32)config.ifc_value;
338 	if (count == 0) {
339 		// there are no interfaces yet
340 		return false;
341 	}
342 
343 	void *buffer = malloc(count * sizeof(struct ifreq));
344 	if (buffer == NULL) {
345 		fprintf(stderr, "%s: Out of memory.\n", Name());
346 		return false;
347 	}
348 
349 	config.ifc_len = count * sizeof(struct ifreq);
350 	config.ifc_buf = buffer;
351 	if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
352 		return false;
353 
354 	ifreq *interface = (ifreq *)buffer;
355 	int32 nameLength = strlen(name);
356 	bool success = false;
357 
358 	for (uint32 i = 0; i < count; i++) {
359 		if (!strncmp(interface->ifr_name, name, nameLength)) {
360 			success = true;
361 			break;
362 		}
363 
364 		interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + interface->ifr_addr.sa_len);
365 	}
366 
367 	free(buffer);
368 	return success;
369 }
370 
371 
372 status_t
373 NetServer::_ConfigureInterface(int socket, BMessage& interface, bool fromMessage)
374 {
375 	const char *device;
376 	if (interface.FindString("device", &device) != B_OK)
377 		return B_BAD_VALUE;
378 
379 	ifreq request;
380 	if (!prepare_request(request, device))
381 		return B_ERROR;
382 
383 	bool startAutoConfig = false;
384 
385 	int32 flags;
386 	if (interface.FindInt32("flags", &flags) < B_OK)
387 		flags = IFF_UP;
388 
389 	int32 mtu;
390 	if (interface.FindInt32("mtu", &mtu) < B_OK)
391 		mtu = -1;
392 
393 	int32 metric;
394 	if (interface.FindInt32("metric", &metric) < B_OK)
395 		metric = -1;
396 
397 	BMessage addressMessage;
398 	for (int32 index = 0; interface.FindMessage("address", index, &addressMessage) == B_OK;
399 			index++) {
400 		const char* family;
401 		if (addressMessage.FindString("family", &family) < B_OK)
402 			continue;
403 
404 		int32 familyIndex;
405 		if (!get_family_index(family, familyIndex)) {
406 			// we don't support this family
407 			continue;
408 		}
409 
410 		int familySocket = socket;
411 		if (family_at_index(familyIndex) != AF_INET)
412 			socket = ::socket(family_at_index(familyIndex), SOCK_DGRAM, 0);
413 		if (socket < 0) {
414 			// the family is not available in this environment
415 			continue;
416 		}
417 
418 		uint32 interfaceIndex = 0;
419 		if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0)
420 			interfaceIndex = request.ifr_index;
421 
422 		if (interfaceIndex == 0) {
423 			// we need to create the interface first
424 			request.ifr_parameter.base_name[0] = '\0';
425 			request.ifr_parameter.device[0] = '\0';
426 			request.ifr_parameter.sub_type = 0;
427 				// the default device is okay for us
428 
429 			if (ioctl(socket, SIOCAIFADDR, &request, sizeof(request)) < 0) {
430 				fprintf(stderr, "%s: Could not add interface: %s\n", Name(),
431 					strerror(errno));
432 				return errno;
433 			}
434 		}
435 
436 		// retrieve addresses
437 
438 		bool autoConfig;
439 		if (addressMessage.FindBool("auto config", &autoConfig) != B_OK)
440 			autoConfig = false;
441 		if (autoConfig && fromMessage) {
442 			// we don't accept auto-config messages this way
443 			continue;
444 		}
445 
446 		bool hasAddress = false, hasMask = false, hasPeer = false, hasBroadcast = false;
447 		struct sockaddr address, mask, peer, broadcast, gateway;
448 		const char* string;
449 
450 		if (!autoConfig) {
451 			if (addressMessage.FindString("address", &string) == B_OK
452 				&& parse_address(familyIndex, string, address)) {
453 				hasAddress = true;
454 
455 				if (addressMessage.FindString("mask", &string) == B_OK
456 					&& parse_address(familyIndex, string, mask))
457 					hasMask = true;
458 			}
459 			if (addressMessage.FindString("peer", &string) == B_OK
460 				&& parse_address(familyIndex, string, peer))
461 				hasPeer = true;
462 			if (addressMessage.FindString("broadcast", &string) == B_OK
463 				&& parse_address(familyIndex, string, broadcast))
464 				hasBroadcast = true;
465 		}
466 
467 		route_entry route;
468 		memset(&route, 0, sizeof(route_entry));
469 		route.flags = RTF_STATIC | RTF_DEFAULT;
470 
471 		request.ifr_route = route;
472 		ioctl(socket, SIOCDELRT, &request, sizeof(request));
473 			// Try to remove a previous default route, doesn't matter
474 			// if it fails.
475 
476 		if (autoConfig) {
477 			// add a default route to make the interface accessible, even without an address
478 			if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) {
479 				fprintf(stderr, "%s: Could not add route for %s: %s\n",
480 					Name(), device, strerror(errno));
481 			} else {
482 				_QuitLooperForDevice(device);
483 				startAutoConfig = true;
484 			}
485 		} else if (addressMessage.FindString("gateway", &string) == B_OK
486 			&& parse_address(familyIndex, string, gateway)) {
487 			// add gateway route, if we're asked for it
488 			route.flags = RTF_STATIC | RTF_DEFAULT | RTF_GATEWAY;
489 			route.gateway = &gateway;
490 
491 			request.ifr_route = route;
492 			if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) {
493 				fprintf(stderr, "%s: Could not add route for %s: %s\n",
494 					Name(), device, strerror(errno));
495 			}
496 		}
497 
498 		// set addresses
499 
500 		if (hasAddress) {
501 			memcpy(&request.ifr_addr, &address, address.sa_len);
502 
503 			if (ioctl(familySocket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) {
504 				fprintf(stderr, "%s: Setting address failed: %s\n", Name(), strerror(errno));
505 				continue;
506 			}
507 		}
508 
509 		if (ioctl(familySocket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) < 0) {
510 			fprintf(stderr, "%s: Getting flags failed: %s\n", Name(), strerror(errno));
511 			continue;
512 		}
513 		int32 currentFlags = request.ifr_flags;
514 
515 		if (!hasMask && hasAddress && family_at_index(familyIndex) == AF_INET
516 			&& ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0
517 			&& request.ifr_mask.sa_family == AF_UNSPEC) {
518 			// generate standard netmask if it doesn't have one yet
519 			sockaddr_in *netmask = (sockaddr_in *)&mask;
520 			netmask->sin_len = sizeof(sockaddr_in);
521 			netmask->sin_family = AF_INET;
522 
523 			// choose default netmask depending on the class of the address
524 			in_addr_t net = ((sockaddr_in *)&address)->sin_addr.s_addr;
525 			if (IN_CLASSA(net)
526 				|| (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
527 				// class A, or loopback
528 				netmask->sin_addr.s_addr = IN_CLASSA_NET;
529 			} else if (IN_CLASSB(net)) {
530 				// class B
531 				netmask->sin_addr.s_addr = IN_CLASSB_NET;
532 			} else {
533 				// class C and rest
534 				netmask->sin_addr.s_addr = IN_CLASSC_NET;
535 			}
536 
537 			hasMask = true;
538 		}
539 		if (hasMask) {
540 			memcpy(&request.ifr_mask, &mask, mask.sa_len);
541 
542 			if (ioctl(familySocket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) {
543 				fprintf(stderr, "%s: Setting subnet mask failed: %s\n", Name(), strerror(errno));
544 				continue;
545 			}
546 		}
547 
548 		if (!hasBroadcast && hasAddress && (currentFlags & IFF_BROADCAST)
549 			&& family_at_index(familyIndex) == AF_INET
550 			&& ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0
551 			&& request.ifr_mask.sa_family == AF_UNSPEC) {
552 				// generate standard broadcast address if it doesn't have one yet
553 			sockaddr_in *broadcastAddr = (sockaddr_in *)&broadcast;
554 			uint32 maskValue = ((sockaddr_in *)&mask)->sin_addr.s_addr;
555 			uint32 broadcastValue = ((sockaddr_in *)&address)->sin_addr.s_addr;
556 			broadcastValue = (broadcastValue & maskValue) | ~maskValue;
557 			broadcastAddr->sin_len = sizeof(sockaddr_in);
558 			broadcastAddr->sin_family = AF_INET;
559 			broadcastAddr->sin_addr.s_addr = broadcastValue;
560 			hasBroadcast = true;
561 		}
562 		if (hasBroadcast) {
563 			memcpy(&request.ifr_broadaddr, &broadcast, broadcast.sa_len);
564 
565 			if (ioctl(familySocket, SIOCSIFBRDADDR, &request, sizeof(struct ifreq)) < 0) {
566 				fprintf(stderr, "%s: Setting broadcast address failed: %s\n", Name(), strerror(errno));
567 				continue;
568 			}
569 		}
570 
571 		if (hasPeer) {
572 			memcpy(&request.ifr_dstaddr, &peer, peer.sa_len);
573 
574 			if (ioctl(familySocket, SIOCSIFDSTADDR, &request, sizeof(struct ifreq)) < 0) {
575 				fprintf(stderr, "%s: Setting peer address failed: %s\n", Name(), strerror(errno));
576 				continue;
577 			}
578 		}
579 
580 		// set flags
581 
582 		if (flags != 0) {
583 			request.ifr_flags = currentFlags | flags;
584 			if (ioctl(familySocket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)) < 0)
585 				fprintf(stderr, "%s: Setting flags failed: %s\n", Name(), strerror(errno));
586 		}
587 
588 		// set options
589 
590 		if (mtu != -1) {
591 			request.ifr_mtu = mtu;
592 			if (ioctl(familySocket, SIOCSIFMTU, &request, sizeof(struct ifreq)) < 0)
593 				fprintf(stderr, "%s: Setting MTU failed: %s\n", Name(), strerror(errno));
594 		}
595 
596 		if (metric != -1) {
597 			request.ifr_metric = metric;
598 			if (ioctl(familySocket, SIOCSIFMETRIC, &request, sizeof(struct ifreq)) < 0)
599 				fprintf(stderr, "%s: Setting metric failed: %s\n", Name(), strerror(errno));
600 		}
601 	}
602 
603 	if (startAutoConfig) {
604 		// start auto configuration
605 		AutoconfigLooper* looper = new AutoconfigLooper(this, device);
606 		looper->Run();
607 
608 		fDeviceMap[device] = looper;
609 	}
610 
611 	return B_OK;
612 }
613 
614 
615 bool
616 NetServer::_QuitLooperForDevice(const char* device)
617 {
618 	LooperMap::iterator iterator = fDeviceMap.find(device);
619 	if (iterator == fDeviceMap.end())
620 		return false;
621 
622 	// there is a looper for this device - quit it
623 	iterator->second->Lock();
624 	iterator->second->Quit();
625 
626 	fDeviceMap.erase(iterator);
627 	return true;
628 }
629 
630 
631 BLooper*
632 NetServer::_LooperForDevice(const char* device)
633 {
634 	LooperMap::const_iterator iterator = fDeviceMap.find(device);
635 	if (iterator == fDeviceMap.end())
636 		return NULL;
637 
638 	return iterator->second;
639 }
640 
641 
642 status_t
643 NetServer::_ConfigureDevice(int socket, const char* path)
644 {
645 
646 	// bring interface up, but don't configure it just yet
647 	BMessage interface;
648 	interface.AddString("device", path);
649 	BMessage address;
650 	address.AddString("family", "inet");
651 	address.AddBool("auto config", true);
652 	interface.AddMessage("address", &address);
653 
654 	return _ConfigureInterface(socket, interface);
655 }
656 
657 
658 void
659 NetServer::_ConfigureDevices(int socket, const char* startPath)
660 {
661 	BDirectory directory(startPath);
662 	BEntry entry;
663 	while (directory.GetNextEntry(&entry) == B_OK) {
664 		char name[B_FILE_NAME_LENGTH];
665 		struct stat stat;
666 		BPath path;
667 		if (entry.GetName(name) != B_OK
668 			|| !strcmp(name, "stack")
669 			|| entry.GetPath(&path) != B_OK
670 			|| entry.GetStat(&stat) != B_OK)
671 			continue;
672 
673 		if (S_ISBLK(stat.st_mode) || S_ISCHR(stat.st_mode))
674 			_ConfigureDevice(socket, path.Path());
675 		else if (entry.IsDirectory())
676 			_ConfigureDevices(socket, path.Path());
677 	}
678 }
679 
680 
681 void
682 NetServer::_ConfigureInterfaces(int socket)
683 {
684 	BMessage interface;
685 	uint32 cookie = 0;
686 	while (fSettings.GetNextInterface(cookie, interface) == B_OK) {
687 		const char *device;
688 		if (interface.FindString("device", &device) != B_OK)
689 			continue;
690 
691 		if (!strncmp(device, "/dev/net/", 9)) {
692 			// it's a kernel device, check if it's present
693 			BEntry entry(device);
694 			if (!entry.Exists())
695 				continue;
696 		}
697 
698 		_ConfigureInterface(socket, interface);
699 	}
700 }
701 
702 
703 void
704 NetServer::_BringUpInterfaces()
705 {
706 	// we need a socket to talk to the networking stack
707 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
708 	if (socket < 0) {
709 		fprintf(stderr, "%s: The networking stack doesn't seem to be available.\n",
710 			Name());
711 		Quit();
712 		return;
713 	}
714 
715 	// First, we look into the settings, and try to bring everything up from there
716 
717 	_ConfigureInterfaces(socket);
718 
719 	// check configuration
720 
721 	if (!_TestForInterface(socket, "loop")) {
722 		// there is no loopback interface, create one
723 		BMessage interface;
724 		interface.AddString("device", "loop");
725 		BMessage address;
726 		address.AddString("family", "inet");
727 		address.AddString("address", "127.0.0.1");
728 		interface.AddMessage("address", &address);
729 
730 		_ConfigureInterface(socket, interface);
731 	}
732 
733 	// TODO: also check if the networking driver is correctly initialized!
734 	//	(and check for other devices to take over its configuration)
735 
736 	if (!_TestForInterface(socket, "/dev/net/")) {
737 		// there is no driver configured - see if there is one and try to use it
738 		_ConfigureDevices(socket, "/dev/net");
739 	}
740 
741 	close(socket);
742 }
743 
744 
745 void
746 NetServer::_StartServices()
747 {
748 	BHandler* services = new (std::nothrow) Services(fSettings.Services());
749 	if (services != NULL) {
750 		AddHandler(services);
751 		fServices = BMessenger(services);
752 	}
753 }
754 
755 
756 //	#pragma mark -
757 
758 
759 int
760 main()
761 {
762 	NetServer app;
763 	app.Run();
764 
765 	return 0;
766 }
767 
768