int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) { int ret; u8 *buf; LOG_TRACE(priv, FW_MSG, "Sending hcmd:\n"); /* add padding to 256 for IWMC */ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256; LOG_HEXDUMP(FW_MSG, cmd, len); if (len > FW_HCMD_BLOCK_SIZE) { LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n", len, FW_HCMD_BLOCK_SIZE); return -1; } buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL); if (!buf) { LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n", FW_HCMD_BLOCK_SIZE); return -1; } memcpy(buf, cmd, len); ret = iwmct_tx(priv, buf, FW_HCMD_BLOCK_SIZE); kfree(buf); return ret; }
static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) { struct iwmct_parser *parser = &priv->parser; struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; int ret; u32 cmd; LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); memset(parser->buf, 0, parser->buf_size); cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; if (jump) { cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS; hdr->target_addr = cpu_to_le32(parser->entry_point); LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n", parser->entry_point); } else { cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS; LOG_INFO(priv, FW_DOWNLOAD, "last command\n"); } hdr->cmd = cpu_to_le32(cmd); LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr)); /* send it down */ /* TODO: add more proper sending and error checking */ ret = iwmct_tx(priv, parser->buf, IWMC_SDIO_BLK_SIZE); if (ret) LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret); LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return 0; }
static void iwmct_irq_read_worker(struct work_struct *ws) { struct iwmct_priv *priv; struct iwmct_work_struct *read_req; __le32 *buf = NULL; int ret; int iosize; u32 barker; bool is_barker; priv = container_of(ws, struct iwmct_priv, isr_worker); LOG_TRACE(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws); /* --------------------- Handshake with device -------------------- */ sdio_claim_host(priv->func); /* all list manipulations have to be protected by * sdio_claim_host/sdio_release_host */ if (list_empty(&priv->read_req_list)) { LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n"); goto exit_release; } read_req = list_entry(priv->read_req_list.next, struct iwmct_work_struct, list); list_del(&read_req->list); iosize = read_req->iosize; kfree(read_req); buf = kzalloc(iosize, GFP_KERNEL); if (!buf) { LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize); goto exit_release; } LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n", iosize, buf, priv->func->num); /* read from device */ ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize); if (ret) { LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret); goto exit_release; } LOG_HEXDUMP(IRQ, (u8 *)buf, iosize); barker = le32_to_cpu(buf[0]); /* Verify whether it's a barker and if not - treat as regular Rx */ if (barker == IWMC_BARKER_ACK || (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) { /* Valid Barker is equal on first 4 dwords */ is_barker = (buf[1] == buf[0]) && (buf[2] == buf[0]) && (buf[3] == buf[0]); if (!is_barker) { LOG_WARNING(priv, IRQ, "Potentially inconsistent barker " "%08X_%08X_%08X_%08X\n", le32_to_cpu(buf[0]), le32_to_cpu(buf[1]), le32_to_cpu(buf[2]), le32_to_cpu(buf[3])); } } else { is_barker = false; } /* Handle Top CommHub message */ if (!is_barker) { sdio_release_host(priv->func); handle_top_message(priv, (u8 *)buf, iosize); goto exit; } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */ if (atomic_read(&priv->dev_sync) == 0) { LOG_ERROR(priv, IRQ, "ACK barker arrived out-of-sync\n"); goto exit_release; } /* Continuing to FW download (after Sync is completed)*/ atomic_set(&priv->dev_sync, 0); LOG_INFO(priv, IRQ, "ACK barker arrived " "- starting FW download\n"); } else { /* REBOOT barker */ LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker); priv->barker = barker; if (barker & BARKER_DNLOAD_SYNC_MSK) { /* Send the same barker back */ ret = __iwmct_tx(priv, buf, iosize); if (ret) { LOG_ERROR(priv, IRQ, "error %d echoing barker\n", ret); goto exit_release; } LOG_INFO(priv, IRQ, "Echoing barker to device\n"); atomic_set(&priv->dev_sync, 1); goto exit_release; } /* Continuing to FW download (without Sync) */ LOG_INFO(priv, IRQ, "No sync requested " "- starting FW download\n"); } sdio_release_host(priv->func); if (priv->dbg.fw_download) iwmct_fw_load(priv); else LOG_ERROR(priv, IRQ, "FW download not allowed\n"); goto exit; exit_release: sdio_release_host(priv->func); exit: kfree(buf); LOG_TRACE(priv, IRQ, "exit iwmct_irq_read_worker\n"); }
static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, size_t sec_size, __le32 addr) { struct iwmct_parser *parser = &priv->parser; struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; const u8 *cur_block = p_sec; size_t sent = 0; int cnt = 0; int ret = 0; u32 cmd = 0; LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n", addr, sec_size); while (sent < sec_size) { int i; u32 chksm = 0; u32 reset = atomic_read(&priv->reset); /* actual FW data */ u32 data_size = min(parser->buf_size - sizeof(*hdr), sec_size - sent); /* Pad to block size */ u32 trans_size = (data_size + sizeof(*hdr) + IWMC_SDIO_BLK_SIZE - 1) & ~(IWMC_SDIO_BLK_SIZE - 1); ++cnt; /* in case of reset, interrupt FW DOWNLAOD */ if (reset) { LOG_INFO(priv, FW_DOWNLOAD, "Reset detected. Abort FW download!!!"); ret = -ECANCELED; goto exit; } memset(parser->buf, 0, parser->buf_size); cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS; cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS; cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS; hdr->data_size = cpu_to_le32(data_size); hdr->target_addr = addr; /* checksum is allowed for sizes divisible by 4 */ if (data_size & 0x3) cmd &= ~CMD_HDR_USE_CHECKSUM_MSK; memcpy(hdr->data, cur_block, data_size); if (cmd & CMD_HDR_USE_CHECKSUM_MSK) { chksm = data_size + le32_to_cpu(addr) + cmd; for (i = 0; i < data_size >> 2; i++) chksm += ((u32 *)cur_block)[i]; hdr->block_chksm = cpu_to_le32(chksm); LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n", hdr->block_chksm); } LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, " "sec_size=%zd, startAddress 0x%X\n", cnt, trans_size, sent, sec_size, addr); if (priv->dbg.dump) LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size); hdr->cmd = cpu_to_le32(cmd); /* send it down */ /* TODO: add more proper sending and error checking */ ret = iwmct_tx(priv, parser->buf, trans_size); if (ret != 0) { LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d\n", ret); goto exit; } addr = cpu_to_le32(le32_to_cpu(addr) + data_size); sent += data_size; cur_block = p_sec + sent; if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) { LOG_INFO(priv, FW_DOWNLOAD, "Block number limit is reached [%d]\n", priv->dbg.blocks); break; } } if (sent < sec_size) ret = -EINVAL; exit: LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return ret; }
int proxy_thread( struct thread_data *td ) { int remote_fd = -1; int state; int n, client_fd; int result = -1; uint32 client_ip; ssize_t writed; int already_authorized = 0; // if some http data coming together with request char *data = NULL; int data_len = 0; #define BUF_SIZE 1504 #define REQ_SIZE 15004 #define clear_buffer() memset(buffer, 0, BUF_SIZE) #define clear_request() memset(request, 0, REQ_SIZE); #define clear_req() memset(&req, 0, sizeof(req)); char buffer[BUF_SIZE]; char last_host[BUF_SIZE]; request = malloc(REQ_SIZE); struct sockaddr_in remote_addr; struct hostent *remote_host; struct timeval timeout; fd_set rfds; FENTER; ASSERT(request); client_fd = td->client_fd; client_ip = td->client_ip; /* fetch the http request headers */ FD_ZERO( &rfds ); FD_SET( (unsigned int) client_fd, &rfds ); timeout.tv_sec = 15; timeout.tv_usec = 0; if ( select(client_fd + 1, &rfds, NULL, NULL, &timeout ) <= 0) { ERR("select() timeout", 0); result = 11; goto exit; } char *end_of_req = NULL; char *preq = request; clear_req(); clear_request(); clear_buffer(); if ( ( n = read(client_fd, buffer, sizeof(buffer)-4) ) <= 0 ) { ERR("read() fail", 0); result = 12; goto exit; } DBG("- recv %d bytes", n); // append memcpy(preq, buffer, n); preq += n; process_request: while ( !(end_of_req = strstr(request, "\r\n\r\n")) ) { DBG("- request without %s", ANSI_WHITE"CRLFCRLF"ANSI_RESET); clear_buffer(); if ( ( n = read(client_fd, buffer, sizeof(buffer)-4) ) <= 0 ) { ERR("read() fail", 0); result = 12; goto exit; } DBG("- recv %d bytes", n); // append memcpy(preq, buffer, n); preq += n; } end_of_req += 4; // first byte after CRLFCRLF if ( (unsigned int)(end_of_req - request) == strlen(request) ) { DBG("no data found in this request", 0); } else { data_len = preq - end_of_req; DBG("%d data bytes found in this request", data_len); data = malloc(data_len); if (!data) { ERR("malloc() %d", data_len); goto exit; } memcpy(data, end_of_req, data_len); } //#define DUMP #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM CLIENT", (unsigned char*)request, preq-request); #else DBG("RECEIVED FROM CLIENT %d bytes", preq-request); #endif if (!parse_request()) { WARN("bad request:", request); return -1; }; if (!already_authorized) { if (is_proxy_authorized()) { already_authorized = 1; } else { proxy_unauthorized(td); goto exit; } } if ( is_syscmd() ) { if ( is_sys_authorized() ) { process_sys_cmd(); goto exit; } else { sys_unauthorized(td); goto exit; } } //LOG_HEXDUMP("BEFORE", (unsigned char*)req.headers, BUF_SIZE); remove_header("Proxy-Authorization"); remove_header("Proxy-Connection"); //LOG_HEXDUMP("AFTER", (unsigned char*)req.headers, BUF_SIZE); /* resolve the http server hostname */ if ( !(req.hostname) || !( remote_host = gethostbyname( req.hostname ) ) ) { WARN("- fail to resolve '%s'", req.hostname); result = 19; goto exit; } if ( req.connect ) { if( td->connect == 1 && req.port != 443 ) { result = 20; goto exit; } } /* connect to the remote server, if not already connected */ if ( 1 || strcmp(req.hostname, last_host) ) { shutdown( remote_fd, 2 ); close( remote_fd ); remote_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP ); if (remote_fd < 0) { WARN("- socket() fail", 0); result = 21; goto exit; } remote_addr.sin_family = AF_INET; remote_addr.sin_port = htons( (unsigned short) req.port ); memcpy( (void *) &remote_addr.sin_addr, (void *) remote_host->h_addr, remote_host->h_length ); if ( connect( remote_fd, (struct sockaddr *) &remote_addr, sizeof( remote_addr ) ) < 0 ) { WARN("- connect() to '%s:%d' fail", req.hostname, req.port); result = 22; goto exit; } DBG("- connected to %s:%d", req.url_host, req.port); memset( last_host, 0, sizeof( last_host ) ); strncpy( last_host, req.hostname, sizeof( last_host ) - 1 ); } else { INFO("reuse current connection", 0); } if ( req.connect ) { /* send HTTP/1.0 200 OK */ snprintf(buffer, sizeof(buffer), "HTTP/1.0 200 OK\r\n\r\n"); if ( (writed = write(client_fd, buffer, 19)) != 19 ) { WARN("write() fail: %d", writed); result = 23; goto exit; } req.sent_to_client += writed; } else { /* Construct and send modified request */ size_t server_req_size = strlen(req.method) + strlen(req.url) + strlen(req.http_ver) + strlen(req.headers) + 16; char *server_req = malloc(server_req_size); if (!server_req) { ERR("malloc() fail: %d", server_req_size); result = 235; goto exit; } memset(server_req, 0, server_req_size); n = snprintf(server_req, server_req_size - 4, "%s %s %s%s", req.method, req.url, req.http_ver, req.headers); if ((writed = write(remote_fd, server_req, n)) != n) { WARN("write() fail: %d", writed); result = 24; goto exit; } req.sent_to_server += writed; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER", (unsigned char*)server_req, n); #else DBG("SENDED TO SERVER %d bytes", n); #endif free(server_req); if ( data ) { // Some additional data was found in initial request if ((writed = write(remote_fd, data, data_len)) != data_len) { WARN("write() fail: %d", writed); result = 35; goto exit; } req.sent_to_server += writed; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER (data)", (unsigned char*)data, data_len); #else DBG("SENDED TO SERVER %d bytes (data)", n); #endif // clear data free(data); data = NULL; data_len = 0; } } /* tunnel the data between the client and the server */ state = 0; while( 1 ) { FD_ZERO( &rfds ); FD_SET( (unsigned int) client_fd, &rfds ); FD_SET( (unsigned int) remote_fd, &rfds ); n = ( client_fd > remote_fd ) ? client_fd : remote_fd; if ( select( n + 1, &rfds, NULL, NULL, NULL ) < 0 ) { ERR("select() fail", 0); result = 25; goto exit; } if ( FD_ISSET( remote_fd, &rfds ) ) { clear_buffer(); if ( ( n = read( remote_fd, buffer, BUF_SIZE-1) ) <= 0 ) { WARN("read() fail: %d", n); result = 26; goto exit; } #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM SERVER (tunneled)", (unsigned char*)buffer, n); #else DBG("RECEIVED FROM SERVER (tunneled) %d bytes", n); #endif state = 1; /* client finished sending data */ if ( (writed = write( client_fd, buffer, n )) != n ) { WARN("write() fail: %d", writed); result = 27; goto exit; } req.sent_to_client += n; #ifdef DUMP LOG_HEXDUMP("SENDED TO CLIENT (tunneled)", (unsigned char*)buffer, n); #else DBG("SENDED TO CLIENT (tunneled) %d bytes", n); #endif } if ( FD_ISSET( client_fd, &rfds ) ) { clear_buffer(); if ( (n = read( client_fd, buffer, BUF_SIZE-1)) <= 0 ) { WARN("read() fail: %d", n); result = 28; goto exit; } #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM CLIENT (tunneled)", (unsigned char*)buffer, n); #else DBG("RECEIVED FROM CLIENT (tunneled) %d bytes", n); #endif if ( state && !req.connect ) { /* new http request */ INFO("- process new http request", 0); syslog_request(); LOG_HEXDUMP("request", (unsigned char*)buffer, n); memset(&req, 0, sizeof(req)); int req_len = MIN((size_t)n, REQ_SIZE-1); strncpy(request, buffer, req_len); preq = request + req_len; goto process_request; } if ((writed = write( remote_fd, buffer, n)) != n ) { WARN("write() fail: %d", writed); result = 29; goto exit; } req.sent_to_server += n; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER (tunneled)", (unsigned char*)buffer, n); #else DBG("SENDED TO SERVER (tunneled) %d bytes", n); #endif } } /* not reached */ exit: shutdown( client_fd, 2 ); shutdown( remote_fd, 2 ); close( client_fd ); close( remote_fd ); syslog_request(); closelog(); if (request) free(request); FLEAVEA("exit with %d code sent to client: %d, to server: %d", result, req.sent_to_client, req.sent_to_server); return( result ); }
int parse_request() { int result = 0; if ( sscanf(request, "%31s %1023s %15s", req.method, req.url, req.http_ver) != 3) { ERR("sscanf() fail", 0); goto end; }; DBG("- method: '%s'", req.method); DBG("- url: '%s'", req.url); DBG("- http_ver: '%s'", req.http_ver); char *headers = strstr(request, "\r\n"); char *end_of_req = strstr(headers, "\r\n\r\n"); if (!end_of_req) { ERR("CRLFCRLF does not found", 0); goto end; } end_of_req[4] = '\0'; // terminate request by 0 char if (headers) { strncpy(req.headers, headers, sizeof(req.headers)-1); DBG("- headers: '%s'", req.headers); } if ( strcmp("CONNECT", req.method) == 0 ) { if (sscanf(req.url, "%s", req.url_host) < 1) { ERR("sscanf() fail", 0); goto end; } req.connect = 1; } else { req.port = 80; if (sscanf(req.url, "%[^:]://%[^/]%[^\r\n]", req.scheme, req.url_host, req.url_path) < 2) { ERR("sscanf() fail", 0); goto end; } } char *colon; if ((colon = strchr(req.url_host, ':'))) { colon++; req.port = atoi(colon); if ( req.port <= 0 ) { ERR("bad port %d", req.port); goto end; } } if (!parse_hostname()) { ERR("parse_hostname() fail", 0); goto end; } result = 1; DBG("- req.scheme: '%s'", req.scheme); DBG("- req.url_host: '%s'", req.url_host); DBG("- req.url_path: '%s'", req.url_path); DBG("- req.port: '%d'", req.port); DBG("- req.hostname: '%s'", req.hostname); end: if (!result) { LOG_HEXDUMP("BAD REQUEST", (unsigned char*)request, strlen(request)+1); } return result; }