Code

b800d7453cdface5cbea2d919113482b9a3d869b
[inkscape.git] / src / dom / io / socket.cpp
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 "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();
103 TcpSocket::TcpSocket(const DOMString &hostnameArg, int port)
105     init();
106     hostname  = hostnameArg;
107     portno    = port;
111 #ifdef HAVE_SSL
113 static void cryptoLockCallback(int mode, int type, const char *file, int line)
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         }
177 static unsigned long cryptoIdCallback()
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;
187 #endif
190 TcpSocket::TcpSocket(const TcpSocket &other)
192     init();
193     sock      = other.sock;
194     hostname  = other.hostname;
195     portno    = other.portno;
198 static bool tcp_socket_inited = false;
200 void TcpSocket::init()
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;
230 TcpSocket::~TcpSocket()
232     disconnect();
235 bool TcpSocket::isConnected()
237     if (!connected || sock < 0)
238         return false;
239     return true;
242 void TcpSocket::enableSSL(bool val)
244     sslEnabled = val;
248 bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg)
250     hostname = hostnameArg;
251     portno   = portnoArg;
252     return connect();
257 #ifdef HAVE_SSL
258 /*
259 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
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;
270 static void infoCallback(const SSL *ssl, int where, int ret)
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         }
286 */
287 #endif
290 bool TcpSocket::startTls()
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;
351 bool TcpSocket::connect()
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;
403 bool TcpSocket::disconnect()
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;
448 bool TcpSocket::setReceiveTimeout(unsigned long millis)
450     receiveTimeout = millis;
451     return true;
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()
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;
485 bool TcpSocket::write(int ch)
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;
524 bool TcpSocket::write(const DOMString &strArg)
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;
565 int TcpSocket::read()
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;
633 bool TcpSocket::readLine(DOMString &result)
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;
653 }  //namespace io
654 }  //namespace dom
655 }  //namespace w3c
656 }  //namespace org
659 //#########################################################################
660 //# E N D    O F    F I L E
661 //#########################################################################