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-2008 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 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
34 #ifdef HAVE_SYS_FILIO_H
35 #include <sys/filio.h> // needed on Solaris 8
36 #endif
38 #include <cstdio>
39 #include "socket.h"
40 #include "dom/util/thread.h"
42 #ifdef __WIN32__
43 #include <windows.h>
44 #else /* unix */
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netdb.h>
49 #include <unistd.h>
50 #include <sys/ioctl.h>
52 #endif
54 #ifdef HAVE_SSL
55 #include <openssl/ssl.h>
56 #include <openssl/err.h>
58 RELAYTOOL_SSL
59 #endif
62 namespace org
63 {
64 namespace w3c
65 {
66 namespace dom
67 {
68 namespace io
69 {
71 static void mybzero(void *s, size_t n)
72 {
73 unsigned char *p = (unsigned char *)s;
74 while (n > 0)
75 {
76 *p++ = (unsigned char)0;
77 n--;
78 }
79 }
81 static void mybcopy(void *src, void *dest, size_t n)
82 {
83 unsigned char *p = (unsigned char *)dest;
84 unsigned char *q = (unsigned char *)src;
85 while (n > 0)
86 {
87 *p++ = *q++;
88 n--;
89 }
90 }
94 //#########################################################################
95 //# T C P C O N N E C T I O N
96 //#########################################################################
98 TcpSocket::TcpSocket()
99 {
100 init();
101 }
104 TcpSocket::TcpSocket(const DOMString &hostnameArg, int port)
105 {
106 init();
107 hostname = hostnameArg;
108 portno = port;
109 }
112 #ifdef HAVE_SSL
114 static void cryptoLockCallback(int mode, int type, const char *file, int line)
115 {
116 //printf("########### LOCK\n");
117 static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
118 const char *errstr = NULL;
120 int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
121 if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
122 {
123 errstr = "invalid mode";
124 goto err;
125 }
127 if (type < 0 || type >= CRYPTO_NUM_LOCKS)
128 {
129 errstr = "type out of bounds";
130 goto err;
131 }
133 if (mode & CRYPTO_LOCK)
134 {
135 if (modes[type])
136 {
137 errstr = "already locked";
138 /* must not happen in a single-threaded program
139 * (would deadlock)
140 */
141 goto err;
142 }
144 modes[type] = rw;
145 }
146 else if (mode & CRYPTO_UNLOCK)
147 {
148 if (!modes[type])
149 {
150 errstr = "not locked";
151 goto err;
152 }
154 if (modes[type] != rw)
155 {
156 errstr = (rw == CRYPTO_READ) ?
157 "CRYPTO_r_unlock on write lock" :
158 "CRYPTO_w_unlock on read lock";
159 }
161 modes[type] = 0;
162 }
163 else
164 {
165 errstr = "invalid mode";
166 goto err;
167 }
169 err:
170 if (errstr)
171 {
172 /* we cannot use bio_err here */
173 fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
174 errstr, mode, type, file, line);
175 }
176 }
178 static unsigned long cryptoIdCallback()
179 {
180 #ifdef __WIN32__
181 unsigned long ret = (unsigned long) GetCurrentThreadId();
182 #else
183 unsigned long ret = (unsigned long) pthread_self();
184 #endif
185 return ret;
186 }
188 #endif
191 TcpSocket::TcpSocket(const TcpSocket &other)
192 {
193 init();
194 sock = other.sock;
195 hostname = other.hostname;
196 portno = other.portno;
197 }
199 static bool tcp_socket_inited = false;
201 void TcpSocket::init()
202 {
203 if (!tcp_socket_inited)
204 {
205 #ifdef __WIN32__
206 WORD wVersionRequested = MAKEWORD( 2, 2 );
207 WSADATA wsaData;
208 WSAStartup( wVersionRequested, &wsaData );
209 #endif
210 #ifdef HAVE_SSL
211 if (libssl_is_present)
212 {
213 sslStream = NULL;
214 sslContext = NULL;
215 CRYPTO_set_locking_callback(cryptoLockCallback);
216 CRYPTO_set_id_callback(cryptoIdCallback);
217 SSL_library_init();
218 SSL_load_error_strings();
219 }
220 #endif
221 tcp_socket_inited = true;
222 }
223 sock = -1;
224 connected = false;
225 hostname = "";
226 portno = -1;
227 sslEnabled = false;
228 receiveTimeout = 0;
229 }
231 TcpSocket::~TcpSocket()
232 {
233 disconnect();
234 }
236 bool TcpSocket::isConnected()
237 {
238 if (!connected || sock < 0)
239 return false;
240 return true;
241 }
243 void TcpSocket::enableSSL(bool val)
244 {
245 sslEnabled = val;
246 }
249 bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg)
250 {
251 hostname = hostnameArg;
252 portno = portnoArg;
253 return connect();
254 }
258 #ifdef HAVE_SSL
259 /*
260 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
261 {
262 char *password = "password";
263 if (bufLen < (int)(strlen(password)+1))
264 return 0;
266 strcpy(buf,password);
267 int ret = strlen(password);
268 return ret;
269 }
271 static void infoCallback(const SSL *ssl, int where, int ret)
272 {
273 switch (where)
274 {
275 case SSL_CB_ALERT:
276 {
277 printf("## %d SSL ALERT: %s\n", where, SSL_alert_desc_string_long(ret));
278 break;
279 }
280 default:
281 {
282 printf("## %d SSL: %s\n", where, SSL_state_string_long(ssl));
283 break;
284 }
285 }
286 }
287 */
288 #endif
291 bool TcpSocket::startTls()
292 {
293 #ifdef HAVE_SSL
294 if (libssl_is_present)
295 {
296 sslStream = NULL;
297 sslContext = NULL;
299 //SSL_METHOD *meth = SSLv23_method();
300 //SSL_METHOD *meth = SSLv3_client_method();
301 SSL_METHOD *meth = TLSv1_client_method();
302 sslContext = SSL_CTX_new(meth);
303 //SSL_CTX_set_info_callback(sslContext, infoCallback);
305 #if 0
306 char *keyFile = "client.pem";
307 char *caList = "root.pem";
308 /* Load our keys and certificates*/
309 if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
310 {
311 fprintf(stderr, "Can't read certificate file\n");
312 disconnect();
313 return false;
314 }
316 SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
318 if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
319 {
320 fprintf(stderr, "Can't read key file\n");
321 disconnect();
322 return false;
323 }
325 /* Load the CAs we trust*/
326 if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
327 {
328 fprintf(stderr, "Can't read CA list\n");
329 disconnect();
330 return false;
331 }
332 #endif
334 /* Connect the SSL socket */
335 sslStream = SSL_new(sslContext);
336 SSL_set_fd(sslStream, sock);
338 if (SSL_connect(sslStream)<=0)
339 {
340 fprintf(stderr, "SSL connect error\n");
341 disconnect();
342 return false;
343 }
345 sslEnabled = true;
346 }
347 #endif /*HAVE_SSL*/
348 return true;
349 }
352 bool TcpSocket::connect()
353 {
354 if (hostname.size()<1)
355 {
356 printf("open: null hostname\n");
357 return false;
358 }
360 if (portno<1)
361 {
362 printf("open: bad port number\n");
363 return false;
364 }
366 sock = socket(PF_INET, SOCK_STREAM, 0);
367 if (sock < 0)
368 {
369 printf("open: error creating socket\n");
370 return false;
371 }
373 char *c_hostname = (char *)hostname.c_str();
374 struct hostent *server = gethostbyname(c_hostname);
375 if (!server)
376 {
377 printf("open: could not locate host '%s'\n", c_hostname);
378 return false;
379 }
381 struct sockaddr_in serv_addr;
382 mybzero((char *) &serv_addr, sizeof(serv_addr));
383 serv_addr.sin_family = AF_INET;
384 mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
385 server->h_length);
386 serv_addr.sin_port = htons(portno);
388 int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
389 if (ret < 0)
390 {
391 printf("open: could not connect to host '%s'\n", c_hostname);
392 return false;
393 }
395 if (sslEnabled)
396 {
397 if (!startTls())
398 return false;
399 }
400 connected = true;
401 return true;
402 }
404 bool TcpSocket::disconnect()
405 {
406 bool ret = true;
407 connected = false;
408 #ifdef HAVE_SSL
409 if (libssl_is_present)
410 {
411 if (sslEnabled)
412 {
413 if (sslStream)
414 {
415 int r = SSL_shutdown(sslStream);
416 switch(r)
417 {
418 case 1:
419 break; /* Success */
420 case 0:
421 case -1:
422 default:
423 //printf("Shutdown failed");
424 ret = false;
425 }
426 SSL_free(sslStream);
427 }
428 if (sslContext)
429 SSL_CTX_free(sslContext);
430 }
431 sslStream = NULL;
432 sslContext = NULL;
433 }
434 #endif /*HAVE_SSL*/
436 #ifdef __WIN32__
437 closesocket(sock);
438 #else
439 ::close(sock);
440 #endif
441 sock = -1;
442 sslEnabled = false;
444 return ret;
445 }
449 bool TcpSocket::setReceiveTimeout(unsigned long millis)
450 {
451 receiveTimeout = millis;
452 return true;
453 }
455 /**
456 * For normal sockets, return the number of bytes waiting to be received.
457 * For SSL, just return >0 when something is ready to be read.
458 */
459 long TcpSocket::available()
460 {
461 if (!isConnected())
462 return -1;
464 long count = 0;
465 #ifdef __WIN32__
466 if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
467 return -1;
468 #else
469 if (ioctl(sock, FIONREAD, &count) != 0)
470 return -1;
471 #endif
472 if (count<=0 && sslEnabled)
473 {
474 #ifdef HAVE_SSL
475 if (libssl_is_present)
476 {
477 return SSL_pending(sslStream);
478 }
479 #endif
480 }
481 return count;
482 }
486 bool TcpSocket::write(int ch)
487 {
488 if (!isConnected())
489 {
490 printf("write: socket closed\n");
491 return false;
492 }
493 unsigned char c = (unsigned char)ch;
495 if (sslEnabled)
496 {
497 #ifdef HAVE_SSL
498 if (libssl_is_present)
499 {
500 int r = SSL_write(sslStream, &c, 1);
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 }
511 #endif
512 }
513 else
514 {
515 if (send(sock, (const char *)&c, 1, 0) < 0)
516 //if (send(sock, &c, 1, 0) < 0)
517 {
518 printf("write: could not send data\n");
519 return false;
520 }
521 }
522 return true;
523 }
525 bool TcpSocket::write(const DOMString &strArg)
526 {
527 DOMString str = strArg;
529 if (!isConnected())
530 {
531 printf("write(str): socket closed\n");
532 return false;
533 }
534 int len = str.size();
536 if (sslEnabled)
537 {
538 #ifdef HAVE_SSL
539 if (libssl_is_present)
540 {
541 int r = SSL_write(sslStream, (unsigned char *)str.c_str(), len);
542 if (r<=0)
543 {
544 switch(SSL_get_error(sslStream, r))
545 {
546 default:
547 printf("SSL write problem");
548 return -1;
549 }
550 }
551 }
552 #endif
553 }
554 else
555 {
556 if (send(sock, str.c_str(), len, 0) < 0)
557 //if (send(sock, &c, 1, 0) < 0)
558 {
559 printf("write: could not send data\n");
560 return false;
561 }
562 }
563 return true;
564 }
566 int TcpSocket::read()
567 {
568 if (!isConnected())
569 return -1;
571 //We'll use this loop for timeouts, so that SSL and plain sockets
572 //will behave the same way
573 if (receiveTimeout > 0)
574 {
575 unsigned long tim = 0;
576 while (true)
577 {
578 int avail = available();
579 if (avail > 0)
580 break;
581 if (tim >= receiveTimeout)
582 return -2;
583 org::w3c::dom::util::Thread::sleep(20);
584 tim += 20;
585 }
586 }
588 //check again
589 if (!isConnected())
590 return -1;
592 unsigned char ch;
593 if (sslEnabled)
594 {
595 #ifdef HAVE_SSL
596 if (libssl_is_present)
597 {
598 if (!sslStream)
599 return -1;
600 int r = SSL_read(sslStream, &ch, 1);
601 unsigned long err = SSL_get_error(sslStream, r);
602 switch (err)
603 {
604 case SSL_ERROR_NONE:
605 break;
606 case SSL_ERROR_ZERO_RETURN:
607 return -1;
608 case SSL_ERROR_SYSCALL:
609 printf("SSL read problem(syscall) %s\n",
610 ERR_error_string(ERR_get_error(), NULL));
611 return -1;
612 default:
613 printf("SSL read problem %s\n",
614 ERR_error_string(ERR_get_error(), NULL));
615 return -1;
616 }
617 }
618 #endif
619 }
620 else
621 {
622 int ret = recv(sock, (char *)&ch, 1, 0);
623 if (ret <= 0)
624 {
625 if (ret<0)
626 printf("read: could not receive data\n");
627 disconnect();
628 return -1;
629 }
630 }
631 return (int)ch;
632 }
634 bool TcpSocket::readLine(DOMString &result)
635 {
636 result = "";
638 while (isConnected())
639 {
640 int ch = read();
641 if (ch<0)
642 return true;
643 else if (ch=='\r') //we want canonical Net '\r\n' , so skip this
644 {}
645 else if (ch=='\n')
646 return true;
647 else
648 result.push_back((char)ch);
649 }
651 return true;
652 }
654 } //namespace io
655 } //namespace dom
656 } //namespace w3c
657 } //namespace org
660 //#########################################################################
661 //# E N D O F F I L E
662 //#########################################################################