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