Code

Add new rearranged /dom directory
[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 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 "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         }
154 static unsigned long cryptoIdCallback()
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;
164 #endif
167 TcpSocket::TcpSocket(const TcpSocket &other)
169     init();
170     sock      = other.sock;
171     hostname  = other.hostname;
172     portno    = other.portno;
175 static bool tcp_socket_inited = false;
177 void TcpSocket::init()
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;
204 TcpSocket::~TcpSocket()
206     disconnect();
209 bool TcpSocket::isConnected()
211     if (!connected || sock < 0)
212         return false;
213     return true;
216 void TcpSocket::enableSSL(bool val)
218     sslEnabled = val;
222 bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg)
224     hostname = hostnameArg;
225     portno   = portnoArg;
226     return connect();
231 #ifdef HAVE_SSL
232 /*
233 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
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;
244 static void infoCallback(const SSL *ssl, int where, int ret)
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         }
260 */
261 #endif
264 bool TcpSocket::startTls()
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;
322 bool TcpSocket::connect()
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;
374 bool TcpSocket::disconnect()
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;
416 bool TcpSocket::setReceiveTimeout(unsigned long millis)
418     receiveTimeout = millis;
419     return true;
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()
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;
450 bool TcpSocket::write(int ch)
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;
486 bool TcpSocket::write(const DOMString &strArg)
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;
524 int TcpSocket::read()
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;
589 bool TcpSocket::readLine(DOMString &result)
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;
609 }  //namespace io
610 }  //namespace dom
611 }  //namespace w3c
612 }  //namespace org
615 //#########################################################################
616 //# E N D    O F    F I L E
617 //#########################################################################