Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //--------------------------------------------------------------------------------------------------
     18 //
     19 //   Module Name: dmSocketConnector.cpp
     20 //
     21 //   General Description: DmBrwConnector socket implementation class implementation file.  This allows
     22 //   DM to send and receive SYNCML data through HTTP protocol.  This implementation is platform independent.
     23 //   It can be used in any UNIX or LINUX machine.  It can allow use under Windows provided that Windows
     24 //   has the unix socket libraries.  This implementation also supports go through proxy server.
     25 //
     26 //   Proxy Usage:
     27 //
     28 //   setenv DM_PROXY_URL=123.567.28.167:1080            // please use IP address
     29 //   setenv DM_PROXY_AUTH="Basic ZTExNjdadsfagewwer"
     30 //
     31 //   Note: The sequences of letters after Basic is the B64 encoding of your
     32 //         userID:userPW.
     33 //
     34 //   Hint: Here is a web site to do B64 encoding:
     35 //         http://www.ericphelps.com/scripting/samples/Decode.htm
     36 
     37 #include <stdlib.h>        // Needed for exit()
     38 #include <string.h>        // Needed for strcpy() and strlen()
     39 #include <sys/types.h>     // Needed for system defined identifiers.
     40 #include <netinet/in.h>    // Needed for internet address structure.
     41 #include <sys/socket.h>    // Needed for socket(), bind(), etc...
     42 #include <arpa/inet.h>     // Needed for inet_ntoa()
     43 #include <unistd.h>        // Needed for close()
     44 #include <stdio.h>
     45 
     46 #include "dmSocketConnector.h"
     47 
     48 #define  BUF_SIZE   50000   // Buffer size
     49 
     50 static int  g_nPrintfEnabled = getenv("DM_NOPRINTF") == NULL;
     51 
     52 /*
     53  *  Creates a socket connector.
     54  *
     55  *  @return a socket connector.
     56  */
     57 DmSocketConnector * DmBrwCreateConnector() {
     58     if ( g_nPrintfEnabled ) printf("\nCreate Socket Connector\n");
     59     DmSocketConnector * socketHandler = NULL;
     60     socketHandler = new DmSocketConnector();
     61     return socketHandler;
     62 }
     63 
     64 /*
     65  *  Destroy a socket connector.
     66  *
     67  *  @param the handler to a socket connector.
     68  *  @return success if no error, otherwise return fail
     69  */
     70 SYNCML_DM_RET_STATUS_T DmBrwDestroyConnector(DmSocketConnector * browser_handler) {
     71     if ( g_nPrintfEnabled ) printf("Destroy Socket Connector\n");
     72     if (browser_handler != NULL) {
     73         delete browser_handler;
     74     }
     75     return SYNCML_DM_SUCCESS;
     76 }
     77 
     78 /*
     79  *  Prepair a socket connection by parsing the URL to get information such
     80  *  as host, port, and path.
     81  *
     82  *  @param url the URL to be parse
     83  *  @param ConRef the connection reference
     84  *  @param AddrType the type of a host address
     85  *  @return success if get all connection information, otherwise return fail
     86  */
     87 SYNCML_DM_RET_STATUS_T DmSocketConnector::Open(CPCHAR url, CPCHAR ConRef, int AddrType) {
     88     if ( g_nPrintfEnabled ) printf("Open URL: %s\n", url);
     89     DMString strURI = url;
     90     DMString strAddrPort;
     91     DMString strURIPath;
     92 
     93     if (parseURL(strURI, strAddrPort, strURIPath)) {
     94         if (parseAddrPort(strAddrPort, ipAddress, portNum)) {
     95             proxy_url = getenv("DM_PROXY_URL");
     96             if (proxy_url != NULL) {
     97                 urlPath = url;
     98                 DMString tmpURL = proxy_url;
     99                 if (!parseAddrPort(tmpURL, socket_ipAddress, socket_portNum)) {
    100                     return SYNCML_DM_FAIL;
    101                 }
    102             } else {
    103                 urlPath += "/";
    104                 urlPath += strURIPath;
    105                 socket_ipAddress = ipAddress;
    106                 socket_portNum = portNum;
    107             }
    108             return SYNCML_DM_SUCCESS;
    109         }
    110     }
    111     return SYNCML_DM_FAIL;
    112 }
    113 
    114 /*
    115  *  Dynamically change a socket connection by parsing a new URL to get information such
    116  *  as host, port, and path.
    117  *
    118  *  @param url the URL to be parse
    119  *  @param ConRef the connection reference
    120  *  @param AddrType the type of a host address
    121  *  @return success if get all connection information, otherwise return fail
    122  */
    123 SYNCML_DM_RET_STATUS_T DmSocketConnector::SetUrl(CPCHAR url, CPCHAR ConRef, int AddrType) {
    124     if ( g_nPrintfEnabled ) printf("Set URL: %s\n", url);
    125     DMString strURI = url;
    126     DMString strAddrPort;
    127     DMString strURIPath;
    128 
    129     if (parseURL(strURI, strAddrPort, strURIPath)) {
    130         if (proxy_url != NULL) {
    131             urlPath = url;
    132         } else {
    133             urlPath = "/";
    134             urlPath += strURIPath;
    135         }
    136     }
    137     return SYNCML_DM_SUCCESS;
    138 }
    139 
    140 /*
    141  *  Sets the HTTP request method such as GET and POST.
    142  *
    143  *  @param method the method to be set
    144  *  @return success if the method is supported, else return fail
    145  */
    146 SYNCML_DM_RET_STATUS_T DmSocketConnector::SetRequestMethod(XPL_HTTP_METHOD_T method) {
    147     requestMethod = method;
    148     if ( method == XPL_HTTP_METHOD_POST ) {
    149         if ( g_nPrintfEnabled ) printf("Request Method: POST   CODE: %d\n\n", method);
    150         sentBuf = "POST ";
    151         sentBuf += urlPath;
    152         sentBuf += " HTTP/1.0\r\n";
    153         sentBuf += "Host: ";
    154         sentBuf += socket_ipAddress;
    155         sentBuf += "\r\n";
    156     } else if ( method == XPL_HTTP_METHOD_GET ) {
    157         if ( g_nPrintfEnabled ) printf("Request Method: GET    CODE: %d\n", method);
    158         sentBuf = "GET ";
    159         sentBuf += urlPath;
    160         sentBuf += " HTTP/1.1\r\n";
    161     } else {
    162       if ( g_nPrintfEnabled ) printf("Error: Request method not supported\n");
    163       return SYNCML_DM_FAIL;
    164     }
    165     return SYNCML_DM_SUCCESS;
    166 }
    167 
    168 /*
    169  *  Sets HTTP request property by appending to the sent buffer.
    170  *  If DM_PROXY_AUTH enviornment variable was set, it will add
    171  *  a proxy authorization properties in every new session.
    172  *
    173  *  @param header_start the name of a property
    174  *  @param value_start_prt the value of the property
    175  *  @return success after append to the sent buffer.
    176  */
    177 SYNCML_DM_RET_STATUS_T DmSocketConnector::SetRequestProperty(CPCHAR props) {
    178     if ( g_nPrintfEnabled ) printf("Property header: %s\n", props);
    179     sentBuf += props;
    180 
    181     if (proxy_auth == NULL && proxy_enable_check) {
    182         proxy_auth = getenv("DM_PROXY_AUTH");       // proxy_auth=Basic userid:passwd
    183         proxy_enable_check = false;
    184     }
    185 
    186     if (proxy_auth != NULL && new_session) {
    187         if ( g_nPrintfEnabled ) printf ("Property header: Proxy-Authorization=%s\n", proxy_auth);
    188         sentBuf += "Proxy-Authorization";
    189         sentBuf += ": ";
    190         sentBuf += proxy_auth;
    191         sentBuf += "\r\n";
    192         new_session = false;
    193     }
    194     return SYNCML_DM_SUCCESS;
    195 }
    196 
    197 /*
    198  *  Send the data through socket and get the response back.
    199  *
    200  *  @data the data to be sent
    201  *  @size the size of the data to be sent
    202  *  @return success if no error when sending and getting response, else return fail
    203  */
    204 SYNCML_DM_RET_STATUS_T DmSocketConnector::Send(CPCHAR data, UINT32 size) {
    205     if (doSend(data, size) == SYNCML_DM_SUCCESS) {
    206         return getResponse();
    207     }
    208     return SYNCML_DM_FAIL;
    209 }
    210 
    211 /*
    212  *  Return the length of the response data.
    213  *
    214  *  @return the response data length
    215  */
    216 UINT32 DmSocketConnector::GetResponseLength() {
    217     if ( g_nPrintfEnabled ) printf("Get response data length: %d\n", responseLength);
    218     return responseLength;
    219 }
    220 
    221 /*
    222  *  Get the response data.
    223  *
    224  *  @param data the data to be fill with response data
    225  *  @param size the size of the data
    226  *  @return success if the data is filled, else return fail
    227  */
    228 SYNCML_DM_RET_STATUS_T DmSocketConnector::GetResponse(char * data, UINT32 size) {
    229     char tmpBuf[50000];
    230     memcpy(data, responseData, responseLength);
    231     if ( requestMethod == XPL_HTTP_METHOD_POST && size < 50000 ) {
    232         memcpy(tmpBuf, responseData, responseLength);
    233         tmpBuf[responseLength]=0;
    234         if ( g_nPrintfEnabled ) printf("\nResponse Body: %s\n", tmpBuf);
    235     }
    236     return SYNCML_DM_SUCCESS;
    237 }
    238 
    239 /*
    240  *  Get the value of a HTTP header feild based on header field name.
    241  *
    242  *  @param field the header field name
    243  *  @param value the value of the header field
    244  *  @return success if found the header field, else fail
    245  *
    246  */
    247 SYNCML_DM_RET_STATUS_T DmSocketConnector::GetHeaderField(CPCHAR field, char ** value) {
    248     bool found = false;
    249 
    250     for (int i = responseHeaders.begin(); i < responseHeaders.end(); i++) {
    251         if (responseHeaders.get_key(i) == field) {
    252             found = true;
    253             break;
    254         }
    255     }
    256 
    257     if (found == true) {
    258        const DMString& s = responseHeaders.get(field).c_str();
    259        *value = (char*)DmtMemAlloc( s.length() + 1 );
    260        strcpy( *value, s );
    261 
    262        if ( g_nPrintfEnabled ) printf("\nGet HeaderField %s : %s\n", field, *value);
    263     } else {
    264         if ( g_nPrintfEnabled ) printf("INFO: Can not find %s\n", field);
    265         return SYNCML_DM_FAIL;
    266     }
    267     return SYNCML_DM_SUCCESS;
    268 }
    269 
    270 /*
    271  *  Return HTTP response code such as 200, 404, and etc.
    272  *
    273  *  @return the HTTP response code
    274  */
    275 XPL_HTTP_CODE_T DmSocketConnector::GetResponseCode() {
    276     if ( g_nPrintfEnabled ) printf("\nGet Response Code: %s \n\n", responseCode.c_str());
    277     return atoi(responseCode.c_str());
    278 }
    279 
    280 /*
    281  *  Close the socket connection.
    282  *
    283  *  @return success if socket closed, else fail.
    284  */
    285 SYNCML_DM_RET_STATUS_T DmSocketConnector::Close() {
    286     if ( g_nPrintfEnabled ) printf("Close Socket Connector\n");
    287     int ret = close(server_s);
    288     if (ret != 0) {
    289         if ( g_nPrintfEnabled ) printf("ERROR: Can not close the socket.\n");
    290 	return SYNCML_DM_FAIL;
    291     }
    292     return SYNCML_DM_SUCCESS;
    293 }
    294 
    295 /*
    296  *  Close current session, but leave the connection open.
    297  *
    298  *  @return success after closed the session
    299  */
    300 SYNCML_DM_RET_STATUS_T DmSocketConnector::CloseReq() {
    301     if ( g_nPrintfEnabled ) printf("Close Socket Session\n");
    302     new_session = true;
    303     return SYNCML_DM_SUCCESS;
    304 }
    305 
    306 /*
    307  *  Giving string and delimiter, the function will store the section of the string until the delimiter
    308  *  in a string item.  This is similar to string tokenizer.
    309  *
    310  *  Ex:  abc:123:xyz      abc,123,and xyz are tokens seperated by a delimiter ':'
    311  *
    312  *  @param strItem the first section of the string until the delimiter
    313  *  @param strReminder the rest of the string without the delimiter
    314  *  @param cDelim the delimiter
    315  *
    316  */
    317 bool DmSocketConnector::DmStringParserGetItem( DMString& strItem, DMString& strReminder, char cDelim ) {
    318     if ( strReminder[0] == 0 ) {
    319         return false;
    320     }
    321     const char* s = strchr( strReminder, cDelim );
    322     int nPos = s ? s - strReminder : -1;
    323     if ( nPos < 0 ) {
    324         strItem = strReminder;
    325         strReminder = "";
    326     } else {
    327         strItem.assign( strReminder, nPos );
    328         strReminder = DMString(s+1);
    329     }
    330     return true;
    331 }
    332 
    333 /*
    334  *  Parse a HTTP response header.
    335  *
    336  *  @param strBuffer  the received data buffer from server that may contain the entire HTTP header
    337  *  @param dataBufSize  number of bytes contained in strBuffer
    338  *  @param strBufRemaining  the updated buffer containging HTTP body (i.e. strBuffer - HTTP header)
    339  *  @param lenRemaining number of bytes in strBufRemaining
    340  *  @return true if HTTP header marker "\r\n\r\n" is found in strBuffer, false otherwise
    341  *
    342  */
    343 bool DmSocketConnector::DmParseHTTPHeader( char* strBuffer, int dataBufSize, char** strBufRemaining, int& lenRemaining) {
    344     // Let's get the response code first
    345     // If we do not see end of HTTP header, do not bother to parse
    346     char* entireHeaderEnd = strstr( strBuffer, "\r\n\r\n" );
    347     if ( !entireHeaderEnd )
    348         return false;
    349 
    350     // Let's get the response code by looking for first space in response
    351     char* pFirstSpace = strstr( strBuffer, " ");
    352     pFirstSpace++;
    353     char tmpBuf[10];
    354     strncpy(tmpBuf, pFirstSpace, 3);
    355     tmpBuf[3]=0;
    356     responseCode = tmpBuf;
    357     char* curPos = strBuffer;
    358     // skip one \r\n
    359     char *headerEnd = strstr(curPos, "\r\n");
    360     curPos = headerEnd + strlen("\r\n");
    361     headerEnd = strstr(curPos, "\r\n");
    362 
    363     // Found an HTTP Header, let's get the name and value pair
    364     while ( headerEnd != NULL ) {
    365         char* pColon = strchr(curPos, ':');
    366         char name[256];
    367         char value[256];
    368         strncpy(name, curPos, pColon-curPos);
    369         name[pColon-curPos]=0;
    370         strncpy(value, pColon+2, headerEnd-pColon-2);
    371         value[headerEnd-pColon-2]=0;
    372         responseHeaders.put(name, value);
    373         if ( headerEnd == entireHeaderEnd )
    374             break;
    375         curPos = headerEnd + strlen("\r\n");
    376         headerEnd = strstr(curPos, "\r\n");
    377     }
    378     *strBufRemaining = entireHeaderEnd + strlen("\r\n\r\n");
    379     lenRemaining = dataBufSize - (*strBufRemaining - strBuffer);
    380     return true;
    381 }
    382 
    383 /*
    384  *  Parse a HTTP response header.
    385  *
    386  *  @param newData  pointer to the buffer containing data from server
    387  *  @param len  the size of the data in the buffer
    388  *  @return true if data is set successfully, false if memory can not be allocated
    389  */
    390 bool DmSocketConnector::SetResponseData(unsigned char* newData, int len) {
    391     if (len == 0) {
    392         return true;
    393     }
    394 
    395     if ( responseData == NULL ) {
    396         responseData = (unsigned char*)malloc(len);
    397         memcpy( responseData, newData, len);
    398     } else {
    399         unsigned char* newPtr = (unsigned char*)malloc(len + responseLength);
    400         memcpy((void*)newPtr, (void*)responseData, responseLength);
    401         memcpy((void*)(newPtr+responseLength), (void*)newData, len);
    402         free(responseData);
    403         responseData = newPtr;
    404     }
    405     return true;
    406 }
    407 
    408 /*
    409  *  Parse the URL into address:port and URL path.
    410  *
    411  *  @param strURI the URI to be parse
    412  *  @param strAddrPort the string to store the address:port
    413  *  @param strURIPath the string to store the path of the URI
    414  *
    415  *  @return true if URI was in right format, else false
    416  */
    417 bool DmSocketConnector::parseURL(DMString strURI, DMString& strAddrPort, DMString& strURIPath) {
    418     int counter = 0;
    419     DMString tmpStr;
    420 
    421     while(DmStringParserGetItem(tmpStr, strURI, '/')) {
    422         if (counter == 0) {
    423             if (strcmp(tmpStr.c_str(), "http:") != 0) {
    424                 return false;
    425             }
    426         } else if (counter == 1) {
    427             if (tmpStr.c_str()[0]!=0/*strcmp(tmpStr.c_str(), "") !=0*/) {
    428                 return false;
    429             }
    430         } else if (counter == 2) {
    431             strAddrPort = tmpStr;
    432             strURIPath = strURI;
    433             return true;
    434         }
    435         counter++;
    436     }
    437     return false;
    438 }
    439 
    440 /*
    441  *  Parse the address:port into address and port.
    442  *
    443  *  @param strAddrPort the string that holds address:port
    444  *  @param strAddr the string that store address
    445  *  @param strPort the string that store port
    446  *
    447  *  @return true after parsing
    448  */
    449 bool DmSocketConnector::parseAddrPort(DMString strAddrPort, DMString& strAddr, DMString& strPort) {
    450     int j = 0;
    451     DMString tmpStr;
    452     while(DmStringParserGetItem(tmpStr, strAddrPort, ':')) {
    453         if (j == 0) {
    454             strAddr = tmpStr;
    455         } else if (j == 1) {
    456             strPort = tmpStr;
    457         }
    458         j++;
    459     }
    460     return true;
    461 }
    462 
    463 /*
    464  *  Prepair HTTP sent with sent data and sent it out through socket.
    465  *
    466  *  @param data the body of the request
    467  *  @param size the size of the request body
    468  *
    469  *  @return success if it sent all data through socket, else fail
    470  */
    471 SYNCML_DM_RET_STATUS_T DmSocketConnector::doSend(CPCHAR data, UINT32 size) {
    472     unsigned int retcode;        // Return code
    473 
    474     if ( g_nPrintfEnabled ) printf("\n[Header: %d bytes]\n%s\n", strlen(sentBuf.c_str()), sentBuf.c_str());
    475     if ( g_nPrintfEnabled ) printf("[Data: %d bytes]\n%s\n\n", strlen(data), data);
    476 
    477     if (size != 0) {
    478         sentBuf += "Content-length: ";
    479 
    480         char size_str[10];
    481         sprintf(size_str, "%d", size);
    482 
    483         sentBuf += size_str;
    484         sentBuf += "\r\n\r\n";
    485     } else {
    486         sentBuf += "Host: ";
    487         sentBuf += socket_ipAddress;
    488         sentBuf += "\r\n\r\n";
    489     }
    490 
    491     server_s = socket(AF_INET, SOCK_STREAM, 0);
    492     server_addr.sin_family = AF_INET;                // Address family to use
    493 
    494     // Port num to use
    495     server_addr.sin_port = htons(atoi(socket_portNum.c_str()));
    496     // IP address to use
    497     server_addr.sin_addr.s_addr = inet_addr(socket_ipAddress);
    498 
    499     if ( g_nPrintfEnabled ) printf("Host: %s  Port: %s\n", socket_ipAddress.c_str(), socket_portNum.c_str());
    500 
    501     // Do a connect (connect() blocks)
    502     retcode = connect(server_s, (struct sockaddr *)&server_addr,
    503                       sizeof(server_addr));
    504 
    505     if (retcode != 0) {
    506         if ( g_nPrintfEnabled ) printf("ERROR: connect() failed \n");
    507         return SYNCML_DM_FAIL;
    508     }
    509 
    510     //memset(out_buf, 0, BUF_SIZE);
    511     //strcpy(out_buf, sentBuf.c_str());
    512 
    513     if ( g_nPrintfEnabled ) printf("Send Size: %d\n", size);
    514     if ( g_nPrintfEnabled ) printf("\nSend\n>>> >>> >>>\n%s%s<<< <<< <<<\n", sentBuf.c_str(), data);
    515 
    516     // Send a request to the server
    517     int ret = send(server_s, sentBuf.c_str(), strlen(sentBuf.c_str()), 0);
    518     ret = send(server_s, data, size, 0);
    519 
    520     if ( g_nPrintfEnabled ) printf("Send Size: %d\n", ret);
    521 
    522     if (ret != -1) {
    523       return SYNCML_DM_SUCCESS;
    524     }
    525     return SYNCML_DM_FAIL;
    526 }
    527 
    528 /*
    529  *  Get HTTP response by parsing the header information and body.
    530  *
    531  *  @return success if the response size is greater than zero, else fail
    532  */
    533 SYNCML_DM_RET_STATUS_T DmSocketConnector::getResponse() {
    534     if ( g_nPrintfEnabled ) printf("\nGet Response\n");
    535     char in_buf[BUF_SIZE];   // Input buffer for response
    536     bool handleHeader = true;
    537     int retcode;
    538     int nBufUsed = 0;
    539     responseBody = "";
    540     responseLength = 0;
    541 
    542     if ( responseData != NULL ) {
    543         free(responseData);
    544         responseData = NULL;
    545     }
    546 
    547     retcode = recv(server_s, in_buf, BUF_SIZE, 0);
    548     if ( g_nPrintfEnabled ) printf("Size: %d\n", retcode);
    549 
    550     while ((retcode > 0) && (retcode != -1)) {
    551         int lenRemaining;
    552         bool bEndHeader;
    553         char* strRemaining;
    554 
    555         if ( handleHeader )  {
    556             bEndHeader = DmParseHTTPHeader( in_buf, retcode, &strRemaining, lenRemaining);
    557             if ( bEndHeader ) {
    558                 handleHeader = false;
    559                 SetResponseData((unsigned char*)strRemaining,lenRemaining);
    560                 responseLength = lenRemaining;
    561                 nBufUsed = 0;
    562             }
    563             else
    564               nBufUsed += retcode;
    565         } else {
    566             SetResponseData((unsigned char*)in_buf,retcode);
    567             responseLength += retcode;
    568         }
    569         retcode = recv(server_s, in_buf + nBufUsed, BUF_SIZE-nBufUsed, 0);
    570     }
    571 
    572     if ( responseLength > 0 ) {
    573         return SYNCML_DM_SUCCESS;
    574     } else {
    575         return SYNCML_DM_FAIL;
    576     }
    577 }
    578