1/* ans.c - Interface for text2atm and atm2text to ANS */
2
3/* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */
4
5
6/*
7 * This stuff is a temporary hack to avoid using gethostbyname_nsap and such
8 * without doing the "full upgrade" to getaddrinfo/getnameinfo. This also
9 * serves as an exercise for me to get all the details right before I propose
10 * a patch that would eventually end up in libc (and that should therefore be
11 * as stable as possible).
12 */
13
14#if HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21
22#include <netinet/in.h>
23#include <arpa/nameser.h>
24#include <netdb.h>
25#include <resolv.h>
26
27#include "atm.h"
28#include "atmres.h"
29
30
31#define MAX_ANSWER 2048
32#define MAX_NAME   1024
33
34#define MAX_LINE		2048	/* in /etc/e164_cc */
35#define E164_CC_DEFAULT_LEN	   2
36#define E164_CC_FILE		"/etc/e164_cc"
37
38#define GET16(pos) (((pos)[0] << 8) | (pos)[1])
39
40
41static int ans(const char *text,int wanted,void *result,int res_len)
42{
43    unsigned char answer[MAX_ANSWER];
44    unsigned char name[MAX_NAME];
45    unsigned char *pos,*data,*found;
46    int answer_len,name_len,data_len,found_len;
47    int questions,answers;
48
49    found_len = 0; /* gcc wants it */
50    if ((answer_len = res_search(text,C_IN,wanted,answer,MAX_ANSWER)) < 0)
51	return TRY_OTHER;
52    /*
53     * Response header: id, flags, #queries, #answers, #authority,
54     * #additional (all 16 bits)
55     */
56    pos = answer+12;
57    if (answer[3] & 15) return TRY_OTHER; /* rcode != 0 */
58    questions = GET16(answer+4);
59    if (questions != 1) return TRY_OTHER; /* trouble ... */
60    answers = GET16(answer+6);
61    if (answers < 1) return TRY_OTHER;
62    /*
63     * Query: name, type (16), class (16)
64     */
65    if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) < 0)
66	return TRY_OTHER;
67    pos += name_len;
68    if (GET16(pos) != wanted || GET16(pos+2) != C_IN) return TRY_OTHER;
69    pos += 4;
70    /*
71     * Iterate over answers until we find something we like, giving priority
72     * to ATMA_AESA (until signaling is fixed to work with E.164 too)
73     */
74    found = NULL;
75    while (answers--) {
76	/*
77	 * RR: name, type (16), class (16), TTL (32), resource_len (16),
78	 * resource_data ...
79	 */
80	if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME))
81	  < 0) return TRY_OTHER;
82	pos += name_len;
83	data_len = GET16(pos+8);
84	data = pos+10;
85	pos = data+data_len;
86	if (GET16(data-10) != wanted || GET16(data-8) != C_IN || !--data_len)
87	    continue;
88	switch (wanted) {
89            case T_NSAP:
90                data_len++;
91                if (data_len != ATM_ESA_LEN) continue;
92                memcpy(((struct sockaddr_atmsvc *) result)->
93                  sas_addr.prv,data,ATM_ESA_LEN);
94                return 0;
95	    case T_ATMA:
96		switch (*data++) {
97		    case ATMA_AESA:
98			if (data_len != ATM_ESA_LEN) continue;
99			memcpy(((struct sockaddr_atmsvc *) result)->
100			  sas_addr.prv,data,ATM_ESA_LEN);
101			return 0;
102		    case ATMA_E164:
103			if (data_len > ATM_E164_LEN) continue;
104			if (!found) {
105			    found = data;
106			    found_len = data_len;
107			}
108			break;
109		    default:
110			continue;
111		}
112	    case T_PTR:
113		    if (dn_expand(answer,answer+answer_len,data,result,
114		      res_len) < 0) return FATAL;
115		    return 0;
116		default:
117		    continue;
118	}
119    }
120    if (!found) return TRY_OTHER;
121    memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found,
122      found_len);
123    ((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0;
124    return 0;
125}
126
127
128int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length,
129  int flags)
130{
131    if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER;
132    memset(addr,0,sizeof(*addr));
133    addr->sas_family = AF_ATMSVC;
134    if (!ans(text,T_ATMA,addr,length)) return 0;
135    return ans(text,T_NSAP,addr,length);
136}
137
138
139static int encode_nsap(char *buf,const unsigned char *addr)
140{
141    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,
142      4,2,0 };
143    static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 };
144    int *fmt;
145    int pos,i,j;
146
147    switch (*addr) {
148	case ATM_AFI_DCC:
149	case ATM_AFI_ICD:
150	case ATM_AFI_LOCAL:
151	case ATM_AFI_DCC_GROUP:
152	case ATM_AFI_ICD_GROUP:
153	case ATM_AFI_LOCAL_GROUP:
154	    fmt = fmt_dcc;
155	    break;
156	case ATM_AFI_E164:
157	case ATM_AFI_E164_GROUP:
158	    fmt = fmt_e164;
159	    break;
160	default:
161	    return TRY_OTHER;
162    }
163    pos = 2*ATM_ESA_LEN;
164    for (i = 0; fmt[i]; i++) {
165	pos -= fmt[i];
166	for (j = 0; j < fmt[i]; j++)
167	    sprintf(buf++,"%x",
168	      (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf);
169	*buf++ = '.';
170    }
171    strcpy(buf,"AESA.ATMA.INT.");
172    return 0;
173}
174
175
176static int encode_nsap_new(char *buf,const unsigned char *addr)
177{
178    int i;
179    int digit;
180
181    for (i = 20; i; ) {
182        i--;
183        digit = addr[i] & 0x0F;
184        *(buf++) = digit + (digit >= 10 ? '7' : '0');
185        *(buf++) = '.';
186        digit = ((unsigned char) (addr[i])) >> 4;
187        *(buf++) = digit + (digit >= 10 ? '7' : '0');
188        *(buf++) = '.';
189    }
190    strcpy (buf, "NSAP.INT.");
191    return 0;
192}
193
194
195static int cc_len(int p0,int p1)
196{
197    static char *cc_table = NULL;
198    FILE *file;
199    char buffer[MAX_LINE];
200    char *here;
201    int cc;
202
203    if (!cc_table) {
204	if (!(cc_table = malloc(100))) {
205	    perror("malloc");
206	    return E164_CC_DEFAULT_LEN;
207	}
208	memset(cc_table,E164_CC_DEFAULT_LEN,100);
209	if (!(file = fopen(E164_CC_FILE,"r")))
210	    perror(E164_CC_FILE);
211	else {
212	    while (fgets(buffer,MAX_LINE,file)) {
213		here = strchr(buffer,'#');
214		if (here) *here = 0;
215		if (sscanf(buffer,"%d",&cc) == 1) {
216		    if (cc < 10) cc_table[cc] = 1;
217		    else if (cc < 100) cc_table[cc] = 2;
218			else cc_table[cc/10] = 3;
219		}
220	    }
221	    fclose(file);
222	}
223    }
224    if (cc_table[p0] == 1) return 1;
225    return cc_table[p0*10+p1];
226}
227
228
229static int encode_e164(char *buf,const char *addr)
230{
231    const char *prefix,*here;
232
233    prefix = addr+cc_len(addr[0]-48,addr[1]-48);
234    here = strchr(addr,0);
235    while (here > prefix) {
236	*buf++ = *--here;
237	*buf++ = '.';
238    }
239    while (here > addr) *buf++ = *addr++;
240    strcpy(buf,".E164.ATMA.INT.");
241    return 0;
242}
243
244
245int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr,
246  int flags)
247{
248    char tmp[MAX_NAME]; /* could be smaller ... */
249    int res;
250
251    if (addr->sas_addr.prv) {
252        res = encode_nsap(tmp,addr->sas_addr.prv);
253        if (!res && !ans(tmp,T_PTR,buffer,length)) return 0;
254	res = encode_nsap_new(tmp,addr->sas_addr.prv);
255        if (res < 0) return res;
256	return ans(tmp,T_PTR,buffer,length);
257    } else {
258        res = encode_e164(tmp,addr->sas_addr.pub);
259        if (res < 0) return res;
260        return ans(tmp,T_PTR,buffer,length);
261    }
262}
263