1 /**
2 * Phoebe DOM Implementation.
3 *
4 * This is a C++ approximation of the W3C DOM model, which follows
5 * fairly closely the specifications in the various .idl files, copies of
6 * which are provided for reference. Most important is this one:
7 *
8 * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
9 *
10 * Authors:
11 * Bob Jamison
12 *
13 * Copyright (C) 2005 Bob Jamison
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
30 #include "socket.h"
31 #include "dom/util/thread.h"
33 #ifdef __WIN32__
34 #include <windows.h>
35 #endif
38 namespace org
39 {
40 namespace w3c
41 {
42 namespace dom
43 {
44 namespace io
45 {
47 static void mybzero(void *s, size_t n)
48 {
49 unsigned char *p = (unsigned char *)s;
50 while (n > 0)
51 {
52 *p++ = (unsigned char)0;
53 n--;
54 }
55 }
57 static void mybcopy(void *src, void *dest, size_t n)
58 {
59 unsigned char *p = (unsigned char *)dest;
60 unsigned char *q = (unsigned char *)src;
61 while (n > 0)
62 {
63 *p++ = *q++;
64 n--;
65 }
66 }
70 //#########################################################################
71 //# T C P C O N N E C T I O N
72 //#########################################################################
74 TcpSocket::TcpSocket()
75 {
76 init();
77 }
80 TcpSocket::TcpSocket(const DOMString &hostnameArg, int port)
81 {
82 init();
83 hostname = hostnameArg;
84 portno = port;
85 }
88 #ifdef HAVE_SSL
90 static void cryptoLockCallback(int mode, int type, const char *file, int line)
91 {
92 //printf("########### LOCK\n");
93 static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
94 const char *errstr = NULL;
96 int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
97 if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
98 {
99 errstr = "invalid mode";
100 goto err;
101 }
103 if (type < 0 || type >= CRYPTO_NUM_LOCKS)
104 {
105 errstr = "type out of bounds";
106 goto err;
107 }
109 if (mode & CRYPTO_LOCK)
110 {
111 if (modes[type])
112 {
113 errstr = "already locked";
114 /* must not happen in a single-threaded program
115 * (would deadlock)
116 */
117 goto err;
118 }
120 modes[type] = rw;
121 }
122 else if (mode & CRYPTO_UNLOCK)
123 {
124 if (!modes[type])
125 {
126 errstr = "not locked";
127 goto err;
128 }
130 if (modes[type] != rw)
131 {
132 errstr = (rw == CRYPTO_READ) ?
133 "CRYPTO_r_unlock on write lock" :
134 "CRYPTO_w_unlock on read lock";
135 }
137 modes[type] = 0;
138 }
139 else
140 {
141 errstr = "invalid mode";
142 goto err;
143 }
145 err:
146 if (errstr)
147 {
148 /* we cannot use bio_err here */
149 fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
150 errstr, mode, type, file, line);
151 }
152 }
154 static unsigned long cryptoIdCallback()
155 {
156 #ifdef __WIN32__
157 unsigned long ret = (unsigned long) GetCurrentThreadId();
158 #else
159 unsigned long ret = (unsigned long) pthread_self();
160 #endif
161 return ret;
162 }
164 #endif
167 TcpSocket::TcpSocket(const TcpSocket &other)
168 {
169 init();
170 sock = other.sock;
171 hostname = other.hostname;
172 portno = other.portno;
173 }
175 static bool tcp_socket_inited = false;
177 void TcpSocket::init()
178 {
179 if (!tcp_socket_inited)
180 {
181 #ifdef __WIN32__
182 WORD wVersionRequested = MAKEWORD( 2, 2 );
183 WSADATA wsaData;
184 WSAStartup( wVersionRequested, &wsaData );
185 #endif
186 #ifdef HAVE_SSL
187 sslStream = NULL;
188 sslContext = NULL;
189 CRYPTO_set_locking_callback(cryptoLockCallback);
190 CRYPTO_set_id_callback(cryptoIdCallback);
191 SSL_library_init();
192 SSL_load_error_strings();
193 #endif
194 tcp_socket_inited = true;
195 }
196 sock = -1;
197 connected = false;
198 hostname = "";
199 portno = -1;
200 sslEnabled = false;
201 receiveTimeout = 0;
202 }
204 TcpSocket::~TcpSocket()
205 {
206 disconnect();
207 }
209 bool TcpSocket::isConnected()
210 {
211 if (!connected || sock < 0)
212 return false;
213 return true;
214 }
216 void TcpSocket::enableSSL(bool val)
217 {
218 sslEnabled = val;
219 }
222 bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg)
223 {
224 hostname = hostnameArg;
225 portno = portnoArg;
226 return connect();
227 }
231 #ifdef HAVE_SSL
232 /*
233 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
234 {
235 char *password = "password";
236 if (bufLen < (int)(strlen(password)+1))
237 return 0;
239 strcpy(buf,password);
240 int ret = strlen(password);
241 return ret;
242 }
244 static void infoCallback(const SSL *ssl, int where, int ret)
245 {
246 switch (where)
247 {
248 case SSL_CB_ALERT:
249 {
250 printf("## %d SSL ALERT: %s\n", where, SSL_alert_desc_string_long(ret));
251 break;
252 }
253 default:
254 {
255 printf("## %d SSL: %s\n", where, SSL_state_string_long(ssl));
256 break;
257 }
258 }
259 }
260 */
261 #endif
264 bool TcpSocket::startTls()
265 {
266 #ifdef HAVE_SSL
267 sslStream = NULL;
268 sslContext = NULL;
270 //SSL_METHOD *meth = SSLv23_method();
271 //SSL_METHOD *meth = SSLv3_client_method();
272 SSL_METHOD *meth = TLSv1_client_method();
273 sslContext = SSL_CTX_new(meth);
274 //SSL_CTX_set_info_callback(sslContext, infoCallback);
276 #if 0
277 char *keyFile = "client.pem";
278 char *caList = "root.pem";
279 /* Load our keys and certificates*/
280 if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
281 {
282 fprintf(stderr, "Can't read certificate file\n");
283 disconnect();
284 return false;
285 }
287 SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
289 if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
290 {
291 fprintf(stderr, "Can't read key file\n");
292 disconnect();
293 return false;
294 }
296 /* Load the CAs we trust*/
297 if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
298 {
299 fprintf(stderr, "Can't read CA list\n");
300 disconnect();
301 return false;
302 }
303 #endif
305 /* Connect the SSL socket */
306 sslStream = SSL_new(sslContext);
307 SSL_set_fd(sslStream, sock);
309 if (SSL_connect(sslStream)<=0)
310 {
311 fprintf(stderr, "SSL connect error\n");
312 disconnect();
313 return false;
314 }
316 sslEnabled = true;
317 #endif /*HAVE_SSL*/
318 return true;
319 }
322 bool TcpSocket::connect()
323 {
324 if (hostname.size()<1)
325 {
326 printf("open: null hostname\n");
327 return false;
328 }
330 if (portno<1)
331 {
332 printf("open: bad port number\n");
333 return false;
334 }
336 sock = socket(PF_INET, SOCK_STREAM, 0);
337 if (sock < 0)
338 {
339 printf("open: error creating socket\n");
340 return false;
341 }
343 char *c_hostname = (char *)hostname.c_str();
344 struct hostent *server = gethostbyname(c_hostname);
345 if (!server)
346 {
347 printf("open: could not locate host '%s'\n", c_hostname);
348 return false;
349 }
351 struct sockaddr_in serv_addr;
352 mybzero((char *) &serv_addr, sizeof(serv_addr));
353 serv_addr.sin_family = AF_INET;
354 mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
355 server->h_length);
356 serv_addr.sin_port = htons(portno);
358 int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
359 if (ret < 0)
360 {
361 printf("open: could not connect to host '%s'\n", c_hostname);
362 return false;
363 }
365 if (sslEnabled)
366 {
367 if (!startTls())
368 return false;
369 }
370 connected = true;
371 return true;
372 }
374 bool TcpSocket::disconnect()
375 {
376 bool ret = true;
377 connected = false;
378 #ifdef HAVE_SSL
379 if (sslEnabled)
380 {
381 if (sslStream)
382 {
383 int r = SSL_shutdown(sslStream);
384 switch(r)
385 {
386 case 1:
387 break; /* Success */
388 case 0:
389 case -1:
390 default:
391 //printf("Shutdown failed");
392 ret = false;
393 }
394 SSL_free(sslStream);
395 }
396 if (sslContext)
397 SSL_CTX_free(sslContext);
398 }
399 sslStream = NULL;
400 sslContext = NULL;
401 #endif /*HAVE_SSL*/
403 #ifdef __WIN32__
404 closesocket(sock);
405 #else
406 ::close(sock);
407 #endif
408 sock = -1;
409 sslEnabled = false;
411 return ret;
412 }
416 bool TcpSocket::setReceiveTimeout(unsigned long millis)
417 {
418 receiveTimeout = millis;
419 return true;
420 }
422 /**
423 * For normal sockets, return the number of bytes waiting to be received.
424 * For SSL, just return >0 when something is ready to be read.
425 */
426 long TcpSocket::available()
427 {
428 if (!isConnected())
429 return -1;
431 long count = 0;
432 #ifdef __WIN32__
433 if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
434 return -1;
435 #else
436 if (ioctl(sock, FIONREAD, &count) != 0)
437 return -1;
438 #endif
439 if (count<=0 && sslEnabled)
440 {
441 #ifdef HAVE_SSL
442 return SSL_pending(sslStream);
443 #endif
444 }
445 return count;
446 }
450 bool TcpSocket::write(int ch)
451 {
452 if (!isConnected())
453 {
454 printf("write: socket closed\n");
455 return false;
456 }
457 unsigned char c = (unsigned char)ch;
459 if (sslEnabled)
460 {
461 #ifdef HAVE_SSL
462 int r = SSL_write(sslStream, &c, 1);
463 if (r<=0)
464 {
465 switch(SSL_get_error(sslStream, r))
466 {
467 default:
468 printf("SSL write problem");
469 return -1;
470 }
471 }
472 #endif
473 }
474 else
475 {
476 if (send(sock, (const char *)&c, 1, 0) < 0)
477 //if (send(sock, &c, 1, 0) < 0)
478 {
479 printf("write: could not send data\n");
480 return false;
481 }
482 }
483 return true;
484 }
486 bool TcpSocket::write(const DOMString &strArg)
487 {
488 DOMString str = strArg;
490 if (!isConnected())
491 {
492 printf("write(str): socket closed\n");
493 return false;
494 }
495 int len = str.size();
497 if (sslEnabled)
498 {
499 #ifdef HAVE_SSL
500 int r = SSL_write(sslStream, (unsigned char *)str.c_str(), len);
501 if (r<=0)
502 {
503 switch(SSL_get_error(sslStream, r))
504 {
505 default:
506 printf("SSL write problem");
507 return -1;
508 }
509 }
510 #endif
511 }
512 else
513 {
514 if (send(sock, str.c_str(), len, 0) < 0)
515 //if (send(sock, &c, 1, 0) < 0)
516 {
517 printf("write: could not send data\n");
518 return false;
519 }
520 }
521 return true;
522 }
524 int TcpSocket::read()
525 {
526 if (!isConnected())
527 return -1;
529 //We'll use this loop for timeouts, so that SSL and plain sockets
530 //will behave the same way
531 if (receiveTimeout > 0)
532 {
533 unsigned long tim = 0;
534 while (true)
535 {
536 int avail = available();
537 if (avail > 0)
538 break;
539 if (tim >= receiveTimeout)
540 return -2;
541 org::w3c::dom::util::Thread::sleep(20);
542 tim += 20;
543 }
544 }
546 //check again
547 if (!isConnected())
548 return -1;
550 unsigned char ch;
551 if (sslEnabled)
552 {
553 #ifdef HAVE_SSL
554 if (!sslStream)
555 return -1;
556 int r = SSL_read(sslStream, &ch, 1);
557 unsigned long err = SSL_get_error(sslStream, r);
558 switch (err)
559 {
560 case SSL_ERROR_NONE:
561 break;
562 case SSL_ERROR_ZERO_RETURN:
563 return -1;
564 case SSL_ERROR_SYSCALL:
565 printf("SSL read problem(syscall) %s\n",
566 ERR_error_string(ERR_get_error(), NULL));
567 return -1;
568 default:
569 printf("SSL read problem %s\n",
570 ERR_error_string(ERR_get_error(), NULL));
571 return -1;
572 }
573 #endif
574 }
575 else
576 {
577 int ret = recv(sock, (char *)&ch, 1, 0);
578 if (ret <= 0)
579 {
580 if (ret<0)
581 printf("read: could not receive data\n");
582 disconnect();
583 return -1;
584 }
585 }
586 return (int)ch;
587 }
589 bool TcpSocket::readLine(DOMString &result)
590 {
591 result = "";
593 while (isConnected())
594 {
595 int ch = read();
596 if (ch<0)
597 return true;
598 else if (ch=='\r') //we want canonical Net '\r\n' , so skip this
599 {}
600 else if (ch=='\n')
601 return true;
602 else
603 result.push_back((char)ch);
604 }
606 return true;
607 }
609 } //namespace io
610 } //namespace dom
611 } //namespace w3c
612 } //namespace org
615 //#########################################################################
616 //# E N D O F F I L E
617 //#########################################################################