/* *=========================================================================== * ipcom_buffer_len *=========================================================================== * Description: Returns the number of bytes of data in the buffer. * Parameters: * Returns: * */ IP_PUBLIC Ip_u32 ipcom_buffer_len(Ipcom_buffer *buffer) { IPCOM_LOG4(DEBUG2, "ipcom_buffer_len :: buf=%x, len=%d, end=%d, offset=%d", buffer, buffer->end - buffer->offset, buffer->end, buffer->offset); return buffer->end - buffer->offset; }
/* *=========================================================================== * ipnet_nat_proxy_dns_transaction_timeout *=========================================================================== * Description: Timeout handler for DNS transactions timeouts * Parameters: cookie - a cookie that is set when the timeout is added. * Returns: nothing. */ IP_STATIC void ipnet_nat_proxy_dns_transaction_timeout(void *cookie) { Ipnet_nat_dns_transaction *trans = cookie; IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_transaction_timeout() :: expired transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); ipcom_list_remove(&trans->list); ipcom_free(trans); }
/* *=========================================================================== * ipcom_buffer_decr_cap *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipcom_buffer_decr_cap(Ipcom_buffer *buffer, Ip_u32 len) { ip_assert( len <= ipcom_buffer_capacity(buffer) ); if(len > ipcom_buffer_capacity(buffer)) return IPCOM_ERR_FAILED; IPCOM_LOG4(DEBUG2, "ipcom_buffer_decr_cap :: buf=%x, len=%d, end=%d, offset=%d", buffer, len, buffer->end, buffer->offset); buffer->end += len; return IPCOM_SUCCESS; }
/* *=========================================================================== * ipcom_buffer_consume_end *=========================================================================== * Description: Consumes the given number of bytes from the end of the buffer. * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipcom_buffer_consume_end(Ipcom_buffer *buffer, Ip_u32 bytes) { ip_assert( bytes <= (buffer->end - buffer->offset) ); if(bytes > (buffer->end - buffer->offset)) return IPCOM_ERR_FAILED; IPCOM_LOG4(DEBUG2, "ipcom_buffer_consume_end :: buf=%x, bytes=%d, end=%d, offset=%d", buffer, bytes, buffer->end, buffer->offset); buffer->end -= bytes; return IPCOM_SUCCESS; }
/* *=========================================================================== * ipnet_nat_proxy_dns_parse_answers *=========================================================================== * Description: Parses and modifies DNS answers in a DNS packet. * Parameters: buf - pointer to buffer with the DNS questions. * buflen - length of buffer with DNS questions. * offset - offset in the buffer where the DNS questions begin. * newlen - pointer to the length of the message if modified. * dns_hdr - pointer to the DNS protocol header. * param - pointer to NAT proxy parameters. * Returns: The number of bytes parsed or -1 if failed. */ IP_STATIC Ip_s32 ipnet_nat_proxy_dns_parse_answers(Ip_u8 *buf, int buflen, int offset, int *newlen, Ipnet_nat_dns_hdr *dns_hdr, Ipnet_nat_proxy_param *param) { int i, count, origoffset, numa; Ip_u16 type, cla, rlen; int newbuflen = sizeof(dnsbuf); Ipnet_nat_dns_transaction *trans; numa = IP_GET_NTOHS(&dns_hdr->no_answ); origoffset = offset; for (i=0; i<numa; i++) { /* Get the name */ count = ipnet_nat_proxy_dns_decode_name(buf, buflen, offset, dnsname, sizeof(dnsname)); if (count < 0) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: could not decode dns name"); return -1; } /* Copy the name */ if (newbuflen - *newlen < count) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } ipcom_memcpy(&dnsbuf[*newlen], buf+offset, count); *newlen += count; offset += count; /* Check space for type, class, ttl and record length */ if (buflen - offset < 10) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: message too short to parse answer"); return -1; } /* Get the type */ type = (Ip_u16)(IP_GET_NTOHS(buf+offset)); switch(type) { case IPNET_NAT_DNS_QTYPE_A: type = IPNET_NAT_DNS_QTYPE_AAAA; break; case IPNET_NAT_DNS_QTYPE_PTR: trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param); if (trans != IP_NULL) { IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_answers() :: found transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); *newlen -= count; /* Move index back to before the name */ count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, trans->ptrname); if (count < 0) { ipnet_nat_proxy_dns_remove_transaction(trans); IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name"); return -1; } *newlen += count; if (i+1 == numa) { /* Remove transaction for last answer */ ipnet_nat_proxy_dns_remove_transaction(trans); } } break; default: break; } /* Copy the type */ if (newbuflen - *newlen < 2) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } IP_SET_HTONS(&dnsbuf[*newlen], type); *newlen += 2; offset += 2; /* Get the class */ cla = (Ip_u16)(IP_GET_NTOHS(buf+offset)); if (cla != IPNET_NAT_DNS_QCLASS_INET) { IPCOM_LOG1(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: unhandled class: %d", cla); return -1; } /* Copy the class */ if (newbuflen - *newlen < 2) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } IP_SET_HTONS(&dnsbuf[*newlen], cla); *newlen += 2; offset += 2; /* Copy the ttl */ if (newbuflen - *newlen < 4) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } ipcom_memcpy(&dnsbuf[*newlen], buf+offset, 4); *newlen += 4; offset += 4; /* Get the record length */ rlen = (Ip_u16)(IP_GET_NTOHS(buf+offset)); if (buflen - offset < rlen) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: message too short to parse answer"); return -1; } /* Copy the record length */ if (newbuflen - *newlen < 2) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } if (type == IPNET_NAT_DNS_QTYPE_AAAA && rlen == 4) { /* Modify record length and make space for the AAAA record */ IP_SET_HTONS(&dnsbuf[*newlen], 16); *newlen += 2; /* Insert AAAA record data */ if (newbuflen - *newlen < 12) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } ipcom_memcpy(&dnsbuf[*newlen], param->prefix, 12); *newlen += 12; } else { IP_SET_HTONS(&dnsbuf[*newlen], rlen); *newlen += 2; } offset += 2; /* Copy the record */ if (newbuflen - *newlen < rlen) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_answers() :: no space left in modified buffer"); return -1; } ipcom_memcpy(&dnsbuf[*newlen], buf+offset, rlen); *newlen += rlen; offset += rlen; } return offset - origoffset; }
/* *=========================================================================== * ipnet_nat_proxy_dns_parse_questions *=========================================================================== * Description: Parses and modifies DNS questions in a DNS packet. * Parameters: buf - pointer to buffer with the DNS questions. * buflen - length of buffer with DNS questions. * offset - offset in the buffer where the DNS questions begin. * newlen - pointer to the length of the message if modified. * dns_hdr - pointer to the DNS protocol header. * param - pointer to NAT proxy parameters. * Returns: The number of bytes parsed or -1 if failed. */ IP_STATIC Ip_s32 ipnet_nat_proxy_dns_parse_questions(Ip_u8 *buf, int buflen, int offset, int *newlen, Ipnet_nat_dns_hdr *dns_hdr, Ipnet_nat_proxy_param *param) { int i, j, k, count, origoffset, numq, numa, request; Ip_u16 type, cla, flags; Ip_u8 addr[4]; Ipnet_nat_dns_transaction *trans; int newbuflen = sizeof(dnsbuf); Ip_u8 c, *zone; numq = IP_GET_NTOHS(&dns_hdr->no_ques); if (numq != 1) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: supports only one question per message"); return -1; } numa = IP_GET_NTOHS(&dns_hdr->no_answ); flags = IP_GET_NTOHS(&dns_hdr->flags); request = (flags & IPNET_NAT_DNS_QR_FLAG) != 0 ? 0 : 1; origoffset = offset; for (i=0; i<numq; i++) { /* Get the name */ count = ipnet_nat_proxy_dns_decode_name(buf, buflen, offset, dnsname, sizeof(dnsname)); if (count < 0) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not decode dns name"); return -1; } /* Copy the name */ if (newbuflen - *newlen < count) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer"); return -1; } ipcom_memcpy(&dnsbuf[*newlen], buf+offset, count); *newlen += count; offset += count; /* Check space for type and class */ if (buflen - offset < 4) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: message too short to parse question"); return -1; } /* Get the type */ type = (Ip_u16)(IP_GET_NTOHS(buf+offset)); switch(type) { case IPNET_NAT_DNS_QTYPE_AAAA: if (request) { trans = ipnet_nat_proxy_dns_add_transaction(IPNET_NAT_DNS_QTYPE_A, dns_hdr, param, IP_NULL); if (trans == IP_NULL) { IPCOM_LOG0(ERR, "ipnet_nat_proxy_dns_parse_questions() :: could not add transaction to list"); return -1; } else { IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: added transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); } type = IPNET_NAT_DNS_QTYPE_A; /* Change type to A */ } break; case IPNET_NAT_DNS_QTYPE_A: if (!request) { trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_A, dns_hdr, param); if (trans != IP_NULL) { IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: found transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); ipnet_nat_proxy_dns_remove_transaction(trans); } else { return -1; } type = IPNET_NAT_DNS_QTYPE_AAAA; /* Change type back to AAAA */ } break; case IPNET_NAT_DNS_QTYPE_PTR: if (request) { zone = (Ip_u8 *)ipcom_strstr((char *)dnsname, "ip6.int"); if (zone == IP_NULL) zone = (Ip_u8 *)ipcom_strstr((char *)dnsname, "ip6.arpa"); if (zone == IP_NULL) { IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: unhandled zone in PTR request"); return -1; } /* Extract IPv4 part */ if (ipcom_strlen((char *)dnsname) != 64 + ipcom_strlen((char *)zone)) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: invalid name in PTR request"); return -1; } k=0; for (j=3; j>=0; j--) { c = ipcom_tolower(dnsname[k]); c = c > '9' ? c - 'a' + 10 : c - '0'; addr[j] = c; k += 2; c = ipcom_tolower(dnsname[k]); c = c > '9' ? c - 'a' + 10 : c - '0'; c <<= 4; addr[j] += c; k += 2; } trans = ipnet_nat_proxy_dns_add_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param, dnsname); if (trans == IP_NULL) { IPCOM_LOG0(ERR, "ipnet_nat_proxy_dns_parse_questions() :: could not add transaction to list"); return -1; } else { IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: added transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); } /* Convert address to PTR name */ if (ipnet_nat_proxy_dns_ptr_name(dnsname, sizeof(dnsname), addr, IP_AF_INET, (Ip_u8 *)"in-addr.arpa") < 0) { ipnet_nat_proxy_dns_remove_transaction(trans); IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode PTR name"); return -1; } *newlen -= count; /* Move index back to before the name */ count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, dnsname); if (count < 0) { ipnet_nat_proxy_dns_remove_transaction(trans); IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name"); return -1; } *newlen += count; } else { trans = ipnet_nat_proxy_dns_find_transaction(IPNET_NAT_DNS_QTYPE_PTR, dns_hdr, param); if (trans != IP_NULL) { IPCOM_LOG4(DEBUG, "ipnet_nat_proxy_dns_parse_questions() :: found transaction:" "id=%d port=%d addr=0x%08x type=%d", trans->id, trans->srcport, trans->dstaddr, trans->type); *newlen -= count; /* Move index back to before the name */ count = ipnet_nat_proxy_dns_encode_name(dnsbuf, newbuflen, *newlen, trans->ptrname); if (count < 0) { ipnet_nat_proxy_dns_remove_transaction(trans); IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: could not encode dns name"); return -1; } *newlen += count; if (numa == 0) { /* Remove the transaction if there are no answers */ ipnet_nat_proxy_dns_remove_transaction(trans); } } else { return -1; } } break; default: return -1; } /* Copy the type */ if (newbuflen - *newlen < 2) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer"); return -1; } IP_SET_HTONS(&dnsbuf[*newlen], type); *newlen += 2; offset += 2; /* Get the class */ cla = (Ip_u16)(IP_GET_NTOHS(buf+offset)); if (cla != IPNET_NAT_DNS_QCLASS_INET) { IPCOM_LOG1(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: unhandled class: %d", cla); return -1; } /* Copy the class */ if (newbuflen - *newlen < 2) { IPCOM_LOG0(WARNING, "ipnet_nat_proxy_dns_parse_questions() :: no space left in modified buffer"); return -1; } IP_SET_HTONS(&dnsbuf[*newlen], cla); *newlen += 2; offset += 2; } return offset - origoffset; }
/* *=========================================================================== * ipnet_config_interface *=========================================================================== * Description: Configures one interface if it exists and is UP. * Parameters: fd - A socket descriptor * ifname - The name of the interface to configure * conf - The configuration to apply to the interface * conf_len - Number of valid entries in the "conf" array * pass - First pass (1) will add all addresses * second pass (2) will add other things like gateways * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_config_interface(Ip_fd fd, char *ifname, char *conf[], int conf_len, int pass) { char *option; char *option_org; char *token; int i; int err; ip_assert(pass == 1 || pass == 2); if (ipcom_if_nametoindex(ifname) == 0) { /* Interface does not exist */ IPCOM_LOG1(INFO, "%s not attached - skipping", ifname); return 0; } if (0 > (err = ipnet_config_bring_up_if(ifname))) { IPCOM_LOG1(INFO, "Failed to bring UP %s", ifname); return 0; } for (i = 0; i < conf_len; i++) { option_org = option = ipcom_strdup(conf[i]); if (option == IP_NULL) return -IP_ERRNO_ENOMEM; token = ipcom_strtok_r(option, " \t/", &option); if (token == IP_NULL || ipcom_strcmp(token, "ifname") == 0) { /* Ignore */ err = 0; } #ifdef IPCOM_USE_INET else if (ipcom_strcmp(token, "inet") == 0) { if (pass == 1) err = ipnet_config_add_inet_addr(fd, ifname, option); } else if (ipcom_strcmp(token, "gateway") == 0) { if (pass == 2) err = ipnet_config_add_gateway(fd, ifname, option); } #else else if (ipcom_strcmp(token, "inet") == 0 || ipcom_strcmp(token, "gateway") == 0) IP_NOOP; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 else if (ipcom_strcmp(token, "inet6") == 0) { if (pass == 1) err = ipnet_config_add_inet6_addr(fd, ifname, option); } else if (ipcom_strcmp(token, "gateway6") == 0) { if (pass == 2) err = ipnet_config_add_gateway6(ifname, option); } #else else if (ipcom_strcmp(token, "inet6") == 0 || ipcom_strcmp(token, "gateway6") == 0) IP_NOOP; #endif /* IPCOM_USE_INET6 */ else if (ipcom_strcmp(token, "devname") != 0) /* Invalid configuration */ err = -IP_ERRNO_EINVAL; ipcom_free(option_org); if (err != 0) { if (err < 0) err = -err; IPCOM_LOG4(ERR, "failed on '%s' when configuring %s: %s (%d)", conf[i], ifname, ipcom_strerror(err), err); IP_PANIC(); break; } } return err; }