socket: use getaddrinfo
[openblackhole/openblackhole-enigma2.git] / lib / network / socket.cpp
1 #include <sys/ioctl.h>
2 #include <asm/ioctls.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <time.h>
6 #include <string.h>
7 #include <linux/serial.h>
8 #include <lib/network/socket.h>
9
10 void eSocket::close()
11 {
12         if (writebuffer.empty())
13         {
14                 int wasconnected = (mystate == Connection) || (mystate == Closing);
15                 rsn = 0;
16                 if (socketdesc >= 0)
17                 {
18                         ::close(socketdesc);
19                         socketdesc = -1;
20                 }
21                 mystate = Invalid;
22                 if (wasconnected)
23                         connectionClosed_();
24         } else
25         {
26                 mystate = Closing;
27                 rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write);
28         }
29 }
30
31 void eSocket::enableRead()
32 {
33         if (rsn)
34                 rsn->setRequested(rsn->getRequested()|eSocketNotifier::Read);
35 }
36
37 void eSocket::disableRead()
38 {
39         if (rsn)
40                 rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Read);
41 }
42
43 void eSocket::inject(const char *data, int len)
44 {
45         readbuffer.write(data, len);
46         if (mystate == Connection)
47                 readyRead_();
48 }
49
50 std::string eSocket::readLine()
51 {
52         int size=readbuffer.searchchr('\n');
53         if (size == -1)
54                 return std::string();
55         size++; // ich will auch das \n
56         char buffer[size+1];
57         buffer[size]=0;
58         readbuffer.read(buffer, size);
59         return std::string(buffer);
60 }
61
62 bool eSocket::canReadLine()
63 {
64         return readbuffer.searchchr('\n') != -1;
65 }
66
67 int eSocket::bytesAvailable()
68 {
69         return readbuffer.size();
70 }
71
72 int eSocket::readBlock(char *data, unsigned int maxlen)
73 {
74         return readbuffer.read(data, maxlen);
75 }
76
77 int eSocket::bytesToWrite()
78 {
79         return writebuffer.size();
80 }
81
82 int eSocket::state()
83 {
84         return mystate;
85 }
86
87 int eSocket::setSocket(int s, int iss)
88 {
89         socketdesc = s;
90         if (socketdesc < 0) return -1;
91         issocket = iss;
92         fcntl(socketdesc, F_SETFL, O_NONBLOCK);
93         last_break = -1;
94
95         rsn = 0;
96         rsn = eSocketNotifier::create(mainloop, getDescriptor(),
97                 eSocketNotifier::Read|eSocketNotifier::Hungup);
98         CONNECT(rsn->activated, eSocket::notifier);
99         return 0;
100 }
101
102 void eSocket::notifier(int what)
103 {
104         if ((what & eSocketNotifier::Read) && (mystate == Connection))
105         {
106                 int bytesavail=256;
107                 if (issocket)
108                         if (ioctl(getDescriptor(), FIONREAD, &bytesavail)<0)
109                                 eDebug("[eSocket] FIONREAD failed.\n");
110
111                 {
112                         if (issocket)
113                         {
114                                 if (!bytesavail)  // does the REMOTE END has closed the connection? (no Hungup here!)
115                                 {
116                                         writebuffer.clear();
117                                         close();
118                                         return;
119                                 }
120                         }
121                         else            // when operating on terminals, check for break
122                         {
123                                 serial_icounter_struct icount;
124                                 memset(&icount, 0, sizeof(icount));
125                                 if (!ioctl(getDescriptor(), TIOCGICOUNT, &icount))
126                                 {
127                                         if (last_break == -1)
128                                                 last_break = icount.brk;
129                                         else if (last_break != icount.brk)
130                                         {
131                                                 last_break = icount.brk;
132                                                 readbuffer.fromfile(getDescriptor(), bytesavail);
133                                                 readbuffer.clear();
134                                                 writebuffer.clear();
135                                                 rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write);
136                                                 write(getDescriptor(), "BREAK!", 6);
137                                                 hangup();
138                                                 return;
139                                         }
140                                 }
141                                 else
142                                         eDebug("[eSocket] TIOCGICOUNT failed: %m");
143                         }
144                         int r;
145                         if ((r=readbuffer.fromfile(getDescriptor(), bytesavail)) != bytesavail)
146                                 if (issocket)
147                                         eDebug("[eSocket] fromfile failed!");
148                         readyRead_();
149                 }
150         } else if (what & eSocketNotifier::Write)
151         {
152                 if ((mystate == Connection) || (mystate == Closing))
153                 {
154                         if (!writebuffer.empty())
155                         {
156                                 bytesWritten_(writebuffer.tofile(getDescriptor(), 65536));
157                                 if (writebuffer.empty())
158                                 {
159                                         rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write);
160                                         if (mystate == Closing)
161                                         {
162                                                 close();                // warning, we might get destroyed after close.
163                                                 return;
164                                         }
165                                 }
166                         } else
167                                 eDebug("[eSocket] got ready to write, but nothin in buffer. strange.");
168                         if (mystate == Closing)
169                                 close();
170                 } else if (mystate == Connecting)
171                 {
172                         mystate=Connection;
173                         rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write);
174
175                         int res;
176                         socklen_t size=sizeof(res);
177                         ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size);
178                         if (!res)
179                                 connected_();
180                         else
181                         {
182                                 close();
183                                 error_(res);
184                         }
185                 }
186         } else if (what & eSocketNotifier::Hungup)
187         {
188                 if (mystate == Connection || (mystate == Closing && issocket) )
189                 {
190                         writebuffer.clear();
191                         close();
192                 } else if (mystate == Connecting)
193                 {
194                         int res;
195                         socklen_t size=sizeof(res);
196                         ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size);
197                         close();
198                         error_(res);
199                 }
200         }
201 }
202
203 int eSocket::writeBlock(const char *data, unsigned int len)
204 {
205         int err=0;
206         int w=len;
207         if (issocket && writebuffer.empty())
208         {
209                 int tw=::send(getDescriptor(), data, len, MSG_NOSIGNAL);
210                 if ((tw < 0) && (errno != EWOULDBLOCK)) {
211         // don't use eDebug here because of a adaptive mutex in the eDebug call..
212         // and eDebug self can cause a call of writeBlock !!
213                         struct timespec tp;
214                         clock_gettime(CLOCK_MONOTONIC, &tp);
215                         fprintf(stderr, "<%6lu.%06lu> [eSocket] write: %m\n", tp.tv_sec, tp.tv_nsec/1000);
216                 }
217                 if (tw < 0)
218                         tw = 0;
219                 data+=tw;
220                 len-=tw;
221         }
222         if (len && !err)
223                 writebuffer.write(data, len);
224
225         if (!writebuffer.empty())
226                 rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write);
227         return w;
228 }
229
230 int eSocket::getDescriptor()
231 {
232         return socketdesc;
233 }
234
235 int eSocket::connect(struct addrinfo *addr)
236 {
237         int res;
238         struct addrinfo *ptr = addr;
239         for (ptr = addr; ptr != NULL; ptr = ptr->ai_next)
240         {
241                 close();
242                 if (setSocket(socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol), 1) < 0)
243                 {
244                         continue;
245                 }
246                 mystate = Idle;
247
248                 res = ::connect(socketdesc, ptr->ai_addr, ptr->ai_addrlen);
249                 if ((res < 0) && (errno != EINPROGRESS) && (errno != EINTR))
250                 {
251                         error_(errno);
252                         continue;
253                 }
254                 if (res < 0)    // EINPROGRESS or EINTR
255                 {
256                         rsn->setRequested(rsn->getRequested() | eSocketNotifier::Write);
257                         mystate = Connecting;
258                         return 0;
259                 }
260                 else
261                 {
262                         mystate = Connection;
263                         connected_();
264                         return 1;
265                 }
266         }
267         return -1;
268 }
269
270 int eSocket::connectToHost(std::string hostname, int port)
271 {
272         int res;
273         struct addrinfo *addr = NULL;
274         struct addrinfo hints;
275         char portnumber[16];
276
277         memset(&hints, 0, sizeof(hints));
278         hints.ai_family = AF_UNSPEC; /* both ipv4 and ipv6 */
279         hints.ai_socktype = SOCK_STREAM;
280         hints.ai_protocol = 0; /* any */
281 #ifdef AI_ADDRCONFIG
282         hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; /* only return ipv6 if we have an ipv6 address ourselves, and ipv4 if we have an ipv4 address ourselves */
283 #else
284         hints.ai_flags = AI_NUMERICSERV; /* AI_ADDRCONFIG is not available */
285 #endif
286         snprintf(portnumber, sizeof(portnumber), "%d", port);
287
288         if ((res = getaddrinfo(hostname.c_str(), portnumber, &hints, &addr)) || !addr)
289         {
290                 eDebug("[eSocket] can't resolve %s (getaddrinfo: %s)", hostname.c_str(), gai_strerror(res));
291                 return -2;
292         }
293
294         res = connect(addr);
295         if (res < 0)
296         {
297                 eDebug("[eSocket] can't connect to host: %s", hostname.c_str());
298         }
299         freeaddrinfo(addr);
300         return res;
301 }
302
303 eSocket::eSocket(eMainloop *ml): readbuffer(32768), writebuffer(32768), mainloop(ml)
304 {
305         mystate = Invalid;
306 }
307
308 eSocket::eSocket(int socket, int issocket, eMainloop *ml): readbuffer(32768), writebuffer(32768), mainloop(ml)
309 {
310         setSocket(socket, issocket);
311         mystate = Connection;
312 }
313
314 eSocket::~eSocket()
315 {
316         if (socketdesc >= 0)
317         {
318                 ::close(socketdesc);
319                 socketdesc = -1;
320         }
321 }
322
323 eUnixDomainSocket::eUnixDomainSocket(eMainloop *ml) : eSocket(ml)
324 {
325 }
326
327 eUnixDomainSocket::eUnixDomainSocket(int socket, int issocket, eMainloop *ml) : eSocket(socket, issocket, ml)
328 {
329 }
330
331 eUnixDomainSocket::~eUnixDomainSocket()
332 {
333 }
334
335 int eUnixDomainSocket::connectToPath(std::string path)
336 {
337         int res;
338         struct sockaddr_un serv_addr_un;
339         struct addrinfo addr;
340
341         memset(&serv_addr_un, 0, sizeof(serv_addr_un));
342         serv_addr_un.sun_family = AF_LOCAL;
343         strcpy(serv_addr_un.sun_path, path.c_str());
344
345         memset(&addr, 0, sizeof(addr));
346         addr.ai_family = AF_LOCAL;
347         addr.ai_socktype = SOCK_STREAM;
348         addr.ai_protocol = 0; /* any */
349         addr.ai_addr = (struct sockaddr *)&serv_addr_un;
350         addr.ai_addrlen = sizeof(serv_addr_un);
351
352         res = connect(&addr);
353         return res;
354 }