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 50static int g_nPrintfEnabled = getenv("DM_NOPRINTF") == NULL; 51 52/* 53 * Creates a socket connector. 54 * 55 * @return a socket connector. 56 */ 57DmSocketConnector * 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 */ 70SYNCML_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 */ 87SYNCML_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 */ 123SYNCML_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 */ 146SYNCML_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 */ 177SYNCML_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 */ 204SYNCML_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 */ 216UINT32 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 */ 228SYNCML_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 */ 247SYNCML_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 */ 275XPL_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 */ 285SYNCML_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 */ 300SYNCML_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 */ 317bool 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 */ 343bool 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 */ 390bool 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 */ 417bool 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 */ 449bool 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 */ 471SYNCML_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 */ 533SYNCML_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