1 /*
2 *
3 * C++ Portable Types Library (PTypes)
4 * Version 2.1.1 Released 27-Jun-2007
5 *
6 * Copyright (C) 2001-2007 Hovik Melikyan
7 *
8 * http://www.melikyan.com/ptypes/
9 *
10 */
11
12 #ifdef WIN32
13 # include <winsock2.h>
14 #else
15 # include <sys/time.h>
16 # include <sys/types.h>
17 # include <sys/socket.h>
18 # include <netinet/in.h>
19 # include <arpa/inet.h>
20 # include <netdb.h>
21 # include <unistd.h>
22 # include <time.h>
23 #endif
24
25 #include "pinet.h"
26
27
28 PTYPES_BEGIN
29
30
31 //
32 // ipbindinfo
33 //
34
35
ipbindinfo(ipaddress iip,const string & ihost,int iport)36 ipbindinfo::ipbindinfo(ipaddress iip, const string& ihost, int iport)
37 : unknown(), ippeerinfo(iip, ihost, iport), handle(invhandle)
38 {
39 }
40
41
~ipbindinfo()42 ipbindinfo::~ipbindinfo()
43 {
44 }
45
46
47 //
48 // ipsvbase
49 //
50
51
ipsvbase(int isocktype)52 ipsvbase::ipsvbase(int isocktype)
53 : socktype(isocktype), active(false), addrlist(true) {}
54
55
~ipsvbase()56 ipsvbase::~ipsvbase()
57 {
58 close();
59 }
60
61
error(ippeerinfo & p,int code,const char * defmsg)62 void ipsvbase::error(ippeerinfo& p, int code, const char* defmsg)
63 {
64 string msg = usockerrmsg(code);
65 if (isempty(msg))
66 msg = defmsg;
67 msg += " [" + p.asstring(true) + ']';
68 throw new estream(nil, code, msg);
69 }
70
71
bind(ipaddress ip,int port)72 int ipsvbase::bind(ipaddress ip, int port)
73 {
74 close();
75 addrlist.add(new ipbindinfo(ip, nullstring, port));
76 return addrlist.get_count() - 1;
77 }
78
79
bindall(int port)80 int ipsvbase::bindall(int port)
81 {
82 close();
83 return bind(ipany, port);
84 }
85
86
clear()87 void ipsvbase::clear()
88 {
89 close();
90 addrlist.clear();
91 }
92
93
open()94 void ipsvbase::open()
95 {
96 close();
97 if (addrlist.get_count() == 0)
98 fatal(CRIT_FIRST + 52, "No addresses specified to bind to");
99 active = true;
100 for (int i = 0; i < addrlist.get_count(); i++)
101 {
102 ipbindinfo* b = addrlist[i];
103 b->handle = ::socket(AF_INET, socktype, 0);
104 if (b->handle < 0)
105 error(*b, usockerrno(), "Couldn't create socket");
106 sockopt(b->handle);
107 dobind(b);
108 }
109 }
110
111
close()112 void ipsvbase::close()
113 {
114 if (!active)
115 return;
116 for (int i = 0; i < addrlist.get_count(); i++)
117 {
118 ipbindinfo* b = addrlist[i];
119 ::closesocket(pexchange(&b->handle, invhandle));
120 }
121 active = false;
122 }
123
124
dopoll(int * i,int timeout)125 bool ipsvbase::dopoll(int* i, int timeout)
126 {
127 fd_set set;
128 setupfds(&set, *i);
129 timeval t;
130 t.tv_sec = timeout / 1000;
131 t.tv_usec = (timeout % 1000) * 1000;
132 if (::select(FD_SETSIZE, &set, nil, nil, (timeout < 0) ? nil : &t) > 0)
133 {
134 if (*i >= 0)
135 return true;
136 // if the user selected -1 (all), find the socket which has a pending connection
137 // and assign it to i
138 for (int j = 0; j < addrlist.get_count(); j++)
139 if (FD_ISSET(uint(addrlist[j]->handle), &set))
140 {
141 *i = j;
142 return true;
143 }
144 }
145 return false;
146 }
147
148
setupfds(void * set,int i)149 void ipsvbase::setupfds(void* set, int i)
150 {
151 #ifdef _MSC_VER
152 // disable "condition always true" warning caused by Microsoft's FD_SET macro
153 # pragma warning (disable: 4127)
154 #endif
155 FD_ZERO((fd_set*)set);
156 if (i >= 0)
157 {
158 int h = get_addr(i).handle;
159 if (h >= 0)
160 FD_SET((uint)h, (fd_set*)set);
161 }
162 else
163 for (i = 0; i < addrlist.get_count(); i++)
164 {
165 int h = addrlist[i]->handle;
166 if (h >= 0)
167 FD_SET((uint)h, (fd_set*)set);
168 }
169 }
170
171
sockopt(int)172 void ipsvbase::sockopt(int)
173 {
174 }
175
176
177 PTYPES_END
178