| /* ans.c - Interface for text2atm and atm2text to ANS */ |
| |
| /* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */ |
| |
| |
| /* |
| * This stuff is a temporary hack to avoid using gethostbyname_nsap and such |
| * without doing the "full upgrade" to getaddrinfo/getnameinfo. This also |
| * serves as an exercise for me to get all the details right before I propose |
| * a patch that would eventually end up in libc (and that should therefore be |
| * as stable as possible). |
| */ |
| |
| #if HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <netinet/in.h> |
| #include <arpa/nameser.h> |
| #include <netdb.h> |
| #include <resolv.h> |
| |
| #include "atm.h" |
| #include "atmres.h" |
| |
| |
| #define MAX_ANSWER 2048 |
| #define MAX_NAME 1024 |
| |
| #define MAX_LINE 2048 /* in /etc/e164_cc */ |
| #define E164_CC_DEFAULT_LEN 2 |
| #define E164_CC_FILE "/etc/e164_cc" |
| |
| #define GET16(pos) (((pos)[0] << 8) | (pos)[1]) |
| |
| |
| static int ans(const char *text,int wanted,void *result,int res_len) |
| { |
| unsigned char answer[MAX_ANSWER]; |
| unsigned char name[MAX_NAME]; |
| unsigned char *pos,*data,*found; |
| int answer_len,name_len,data_len,found_len; |
| int questions,answers; |
| |
| found_len = 0; /* gcc wants it */ |
| if ((answer_len = res_search(text,C_IN,wanted,answer,MAX_ANSWER)) < 0) |
| return TRY_OTHER; |
| /* |
| * Response header: id, flags, #queries, #answers, #authority, |
| * #additional (all 16 bits) |
| */ |
| pos = answer+12; |
| if (answer[3] & 15) return TRY_OTHER; /* rcode != 0 */ |
| questions = GET16(answer+4); |
| if (questions != 1) return TRY_OTHER; /* trouble ... */ |
| answers = GET16(answer+6); |
| if (answers < 1) return TRY_OTHER; |
| /* |
| * Query: name, type (16), class (16) |
| */ |
| if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) < 0) |
| return TRY_OTHER; |
| pos += name_len; |
| if (GET16(pos) != wanted || GET16(pos+2) != C_IN) return TRY_OTHER; |
| pos += 4; |
| /* |
| * Iterate over answers until we find something we like, giving priority |
| * to ATMA_AESA (until signaling is fixed to work with E.164 too) |
| */ |
| found = NULL; |
| while (answers--) { |
| /* |
| * RR: name, type (16), class (16), TTL (32), resource_len (16), |
| * resource_data ... |
| */ |
| if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) |
| < 0) return TRY_OTHER; |
| pos += name_len; |
| data_len = GET16(pos+8); |
| data = pos+10; |
| pos = data+data_len; |
| if (GET16(data-10) != wanted || GET16(data-8) != C_IN || !--data_len) |
| continue; |
| switch (wanted) { |
| case T_NSAP: |
| data_len++; |
| if (data_len != ATM_ESA_LEN) continue; |
| memcpy(((struct sockaddr_atmsvc *) result)-> |
| sas_addr.prv,data,ATM_ESA_LEN); |
| return 0; |
| case T_ATMA: |
| switch (*data++) { |
| case ATMA_AESA: |
| if (data_len != ATM_ESA_LEN) continue; |
| memcpy(((struct sockaddr_atmsvc *) result)-> |
| sas_addr.prv,data,ATM_ESA_LEN); |
| return 0; |
| case ATMA_E164: |
| if (data_len > ATM_E164_LEN) continue; |
| if (!found) { |
| found = data; |
| found_len = data_len; |
| } |
| break; |
| default: |
| continue; |
| } |
| case T_PTR: |
| if (dn_expand(answer,answer+answer_len,data,result, |
| res_len) < 0) return FATAL; |
| return 0; |
| default: |
| continue; |
| } |
| } |
| if (!found) return TRY_OTHER; |
| memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found, |
| found_len); |
| ((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0; |
| return 0; |
| } |
| |
| |
| int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length, |
| int flags) |
| { |
| if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER; |
| memset(addr,0,sizeof(*addr)); |
| addr->sas_family = AF_ATMSVC; |
| if (!ans(text,T_ATMA,addr,length)) return 0; |
| return ans(text,T_NSAP,addr,length); |
| } |
| |
| |
| static int encode_nsap(char *buf,const unsigned char *addr) |
| { |
| static int fmt_dcc[] = { 2,12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, |
| 4,2,0 }; |
| static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 }; |
| int *fmt; |
| int pos,i,j; |
| |
| switch (*addr) { |
| case ATM_AFI_DCC: |
| case ATM_AFI_ICD: |
| case ATM_AFI_LOCAL: |
| case ATM_AFI_DCC_GROUP: |
| case ATM_AFI_ICD_GROUP: |
| case ATM_AFI_LOCAL_GROUP: |
| fmt = fmt_dcc; |
| break; |
| case ATM_AFI_E164: |
| case ATM_AFI_E164_GROUP: |
| fmt = fmt_e164; |
| break; |
| default: |
| return TRY_OTHER; |
| } |
| pos = 2*ATM_ESA_LEN; |
| for (i = 0; fmt[i]; i++) { |
| pos -= fmt[i]; |
| for (j = 0; j < fmt[i]; j++) |
| sprintf(buf++,"%x", |
| (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf); |
| *buf++ = '.'; |
| } |
| strcpy(buf,"AESA.ATMA.INT."); |
| return 0; |
| } |
| |
| |
| static int encode_nsap_new(char *buf,const unsigned char *addr) |
| { |
| int i; |
| int digit; |
| |
| for (i = 20; i; ) { |
| i--; |
| digit = addr[i] & 0x0F; |
| *(buf++) = digit + (digit >= 10 ? '7' : '0'); |
| *(buf++) = '.'; |
| digit = ((unsigned char) (addr[i])) >> 4; |
| *(buf++) = digit + (digit >= 10 ? '7' : '0'); |
| *(buf++) = '.'; |
| } |
| strcpy (buf, "NSAP.INT."); |
| return 0; |
| } |
| |
| |
| static int cc_len(int p0,int p1) |
| { |
| static char *cc_table = NULL; |
| FILE *file; |
| char buffer[MAX_LINE]; |
| char *here; |
| int cc; |
| |
| if (!cc_table) { |
| if (!(cc_table = malloc(100))) { |
| perror("malloc"); |
| return E164_CC_DEFAULT_LEN; |
| } |
| memset(cc_table,E164_CC_DEFAULT_LEN,100); |
| if (!(file = fopen(E164_CC_FILE,"r"))) |
| perror(E164_CC_FILE); |
| else { |
| while (fgets(buffer,MAX_LINE,file)) { |
| here = strchr(buffer,'#'); |
| if (here) *here = 0; |
| if (sscanf(buffer,"%d",&cc) == 1) { |
| if (cc < 10) cc_table[cc] = 1; |
| else if (cc < 100) cc_table[cc] = 2; |
| else cc_table[cc/10] = 3; |
| } |
| } |
| fclose(file); |
| } |
| } |
| if (cc_table[p0] == 1) return 1; |
| return cc_table[p0*10+p1]; |
| } |
| |
| |
| static int encode_e164(char *buf,const char *addr) |
| { |
| const char *prefix,*here; |
| |
| prefix = addr+cc_len(addr[0]-48,addr[1]-48); |
| here = strchr(addr,0); |
| while (here > prefix) { |
| *buf++ = *--here; |
| *buf++ = '.'; |
| } |
| while (here > addr) *buf++ = *addr++; |
| strcpy(buf,".E164.ATMA.INT."); |
| return 0; |
| } |
| |
| |
| int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr, |
| int flags) |
| { |
| char tmp[MAX_NAME]; /* could be smaller ... */ |
| int res; |
| |
| if (addr->sas_addr.prv) { |
| res = encode_nsap(tmp,addr->sas_addr.prv); |
| if (!res && !ans(tmp,T_PTR,buffer,length)) return 0; |
| res = encode_nsap_new(tmp,addr->sas_addr.prv); |
| if (res < 0) return res; |
| return ans(tmp,T_PTR,buffer,length); |
| } else { |
| res = encode_e164(tmp,addr->sas_addr.pub); |
| if (res < 0) return res; |
| return ans(tmp,T_PTR,buffer,length); |
| } |
| } |