Code

e39032040c5eea6f72efc6d68ec1f749dae193a6
[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 <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();
104 TcpSocket::TcpSocket(const DOMString &hostnameArg, int port)
106     init();
107     hostname  = hostnameArg;
108     portno    = port;
112 #ifdef HAVE_SSL
114 static void cryptoLockCallback(int mode, int type, const char *file, int line)
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         }
178 static unsigned long cryptoIdCallback()
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;
188 #endif
191 TcpSocket::TcpSocket(const TcpSocket &other)
193     init();
194     sock      = other.sock;
195     hostname  = other.hostname;
196     portno    = other.portno;
199 static bool tcp_socket_inited = false;
201 void TcpSocket::init()
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;
231 TcpSocket::~TcpSocket()
233     disconnect();
236 bool TcpSocket::isConnected()
238     if (!connected || sock < 0)
239         return false;
240     return true;
243 void TcpSocket::enableSSL(bool val)
245     sslEnabled = val;
249 bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg)
251     hostname = hostnameArg;
252     portno   = portnoArg;
253     return connect();
258 #ifdef HAVE_SSL
259 /*
260 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
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;
271 static void infoCallback(const SSL *ssl, int where, int ret)
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         }
287 */
288 #endif
291 bool TcpSocket::startTls()
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;
352 bool TcpSocket::connect()
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;
404 bool TcpSocket::disconnect()
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;
449 bool TcpSocket::setReceiveTimeout(unsigned long millis)
451     receiveTimeout = millis;
452     return true;
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()
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;
486 bool TcpSocket::write(int ch)
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;
525 bool TcpSocket::write(const DOMString &strArg)
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;
566 int TcpSocket::read()
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;
634 bool TcpSocket::readLine(DOMString &result)
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;
654 }  //namespace io
655 }  //namespace dom
656 }  //namespace w3c
657 }  //namespace org
660 //#########################################################################
661 //# E N D    O F    F I L E
662 //#########################################################################