1/*++
2/* NAME
3/*	inet_addr_list 3
4/* SUMMARY
5/*	internet address list manager
6/* SYNOPSIS
7/*	#include <inet_addr_list.h>
8/*
9/*	void	inet_addr_list_init(list)
10/*	INET_ADDR_LIST *list;
11/*
12/*	void	inet_addr_list_append(list,addr)
13/*	INET_ADDR_LIST *list;
14/*	struct sockaddr *addr;
15/*
16/*	void	inet_addr_list_uniq(list)
17/*	INET_ADDR_LIST *list;
18/*
19/*	void	inet_addr_list_free(list)
20/*	INET_ADDR_LIST *list;
21/* DESCRIPTION
22/*	This module maintains simple lists of internet addresses.
23/*
24/*	inet_addr_list_init() initializes a user-provided structure
25/*	so that it can be used by inet_addr_list_append() and by
26/*	inet_addr_list_free().
27/*
28/*	inet_addr_list_append() appends the specified address to
29/*	the specified list, extending the list on the fly.
30/*
31/*	inet_addr_list_uniq() sorts the specified address list and
32/*	eliminates duplicates.
33/*
34/*	inet_addr_list_free() reclaims memory used for the
35/*	specified address list.
36/* LICENSE
37/* .ad
38/* .fi
39/*	The Secure Mailer license must be distributed with this software.
40/* AUTHOR(S)
41/*	Wietse Venema
42/*	IBM T.J. Watson Research
43/*	P.O. Box 704
44/*	Yorktown Heights, NY 10598, USA
45/*--*/
46
47/* System library. */
48
49#include <sys_defs.h>
50#include <sys/socket.h>
51#include <netinet/in.h>
52#include <arpa/inet.h>
53#include <stdlib.h>
54#include <netdb.h>
55
56/* Utility library. */
57
58#include <msg.h>
59#include <mymalloc.h>
60#include <myaddrinfo.h>
61#include <sock_addr.h>
62#include <inet_addr_list.h>
63
64/* inet_addr_list_init - initialize internet address list */
65
66void    inet_addr_list_init(INET_ADDR_LIST *list)
67{
68    int     init_size;
69
70    list->used = 0;
71    list->size = 0;
72    init_size = 2;
73    list->addrs = (struct sockaddr_storage *)
74	mymalloc(sizeof(*list->addrs) * init_size);
75    list->size = init_size;
76}
77
78/* inet_addr_list_append - append address to internet address list */
79
80void    inet_addr_list_append(INET_ADDR_LIST *list,
81			              struct sockaddr * addr)
82{
83    const char *myname = "inet_addr_list_append";
84    MAI_HOSTADDR_STR hostaddr;
85    int     new_size;
86
87    if (msg_verbose > 1) {
88	SOCKADDR_TO_HOSTADDR(addr, SOCK_ADDR_LEN(addr),
89			     &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
90	msg_info("%s: %s", myname, hostaddr.buf);
91    }
92    if (list->used >= list->size) {
93	new_size = list->size * 2;
94	list->addrs = (struct sockaddr_storage *)
95	    myrealloc((char *) list->addrs, sizeof(*list->addrs) * new_size);
96	list->size = new_size;
97    }
98    memcpy(list->addrs + list->used++, addr, SOCK_ADDR_LEN(addr));
99}
100
101/* inet_addr_list_comp - compare addresses */
102
103static int inet_addr_list_comp(const void *a, const void *b)
104{
105
106    /*
107     * In case (struct *) != (void *).
108     */
109    return (sock_addr_cmp_addr(SOCK_ADDR_PTR(a), SOCK_ADDR_PTR(b)));
110}
111
112/* inet_addr_list_uniq - weed out duplicates */
113
114void    inet_addr_list_uniq(INET_ADDR_LIST *list)
115{
116    int     n;
117    int     m;
118
119    /*
120     * Put the identical members right next to each other.
121     */
122    qsort((void *) list->addrs, list->used,
123	  sizeof(list->addrs[0]), inet_addr_list_comp);
124
125    /*
126     * Nuke the duplicates. Postcondition after while loop: m is the largest
127     * index for which list->addrs[n] == list->addrs[m].
128     */
129    for (m = n = 0; m < list->used; m++, n++) {
130	if (m != n)
131	    list->addrs[n] = list->addrs[m];
132	while (m + 1 < list->used
133	       && inet_addr_list_comp((void *) &(list->addrs[n]),
134				      (void *) &(list->addrs[m + 1])) == 0)
135	    m += 1;
136    }
137    list->used = n;
138}
139
140#ifdef __APPLE_OS_X_SERVER__
141void inet_addr_list_clean(INET_ADDR_LIST *list)
142{
143    int n = 0;
144
145    /* remove IPv6 scoped addresses */
146    while (n < list->used) {
147	if (list->addrs[n].ss_family == AF_INET6 &&
148	    SOCK_ADDR_IN6_PTR(&list->addrs[n])->sin6_scope_id != 0) {
149	    memmove(&list->addrs[n], &list->addrs[n + 1],
150		    sizeof list->addrs[0] * (list->used - n - 1));
151	    --list->used;
152	} else
153	    ++n;
154    }
155}
156#endif
157
158/* inet_addr_list_free - destroy internet address list */
159
160void    inet_addr_list_free(INET_ADDR_LIST *list)
161{
162    myfree((char *) list->addrs);
163}
164
165#ifdef TEST
166#include <inet_proto.h>
167
168 /*
169  * Duplicate elimination needs to be tested.
170  */
171#include <inet_addr_host.h>
172
173static void inet_addr_list_print(INET_ADDR_LIST *list)
174{
175    MAI_HOSTADDR_STR hostaddr;
176    struct sockaddr_storage *sa;
177
178    for (sa = list->addrs; sa < list->addrs + list->used; sa++) {
179	SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
180			     &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
181	msg_info("%s", hostaddr.buf);
182    }
183}
184
185int     main(int argc, char **argv)
186{
187    INET_ADDR_LIST list;
188    INET_PROTO_INFO *proto_info;
189
190    proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_ALL);
191    inet_addr_list_init(&list);
192    while (--argc && *++argv)
193	if (inet_addr_host(&list, *argv) == 0)
194	    msg_fatal("host not found: %s", *argv);
195    msg_info("list before sort/uniq");
196    inet_addr_list_print(&list);
197    inet_addr_list_uniq(&list);
198    msg_info("list after sort/uniq");
199    inet_addr_list_print(&list);
200    inet_addr_list_free(&list);
201    return (0);
202}
203
204#endif
205