Imported Upstream version 1.0.10
[platform/upstream/lksctp-tools.git] / src / lib / addrs.c
1 /* SCTP kernel Implementation: User API extensions.
2  *
3  * addrs.c
4  *
5  * Distributed under the terms of the LGPL v2.1 as described in
6  *    http://www.gnu.org/copyleft/lesser.txt 
7  *
8  * This file is part of the user library that offers support for the
9  * SCTP kernel Implementation. The main purpose of this
10  * code is to provide the SCTP Socket API mappings for user
11  * application to interface with the SCTP in kernel.
12  *
13  * This implementation is based on the Socket API Extensions for SCTP
14  * defined in <draft-ietf-tsvwg-sctpsocket-10.txt.
15  *
16  * (C) Copyright IBM Corp. 2003
17  * Copyright (c) 2001-2002 Intel Corp.
18  *
19  * Written or modified by:
20  *  Ardelle Fan     <ardelle.fan@intel.com>
21  *  Sridhar Samudrala <sri@us.ibm.com>
22  *  Ivan Skytte Jørgensen <isj-sctp@i1.dk>
23  */
24
25 #include <malloc.h>
26 #include <netinet/in.h>
27 #include <netinet/sctp.h>
28 #include <string.h>
29 #include <errno.h>
30
31 /* 
32  * Get local/peer addresses using the old API 
33  * Old kernels (2.6.13 and earlier) only support this API but it breaks 32-bit
34  * programs on 64-bit kernels.
35  */
36 static int
37 sctp_getaddrs_old(int sd, sctp_assoc_t id, int optname_num, int optname_old,
38                   struct sockaddr **addrs)
39 {
40         socklen_t len = sizeof(sctp_assoc_t);
41         int cnt, err;
42         struct sctp_getaddrs_old getaddrs;
43
44         cnt = getsockopt(sd, SOL_SCTP, optname_num, &id, &len);
45         if (cnt < 0)
46                 return -1;
47
48         if (0 == cnt) {
49                 *addrs = NULL;
50                 return 0;
51         }
52
53         len = cnt * sizeof(struct sockaddr_in6);
54
55         getaddrs.assoc_id = id;
56         getaddrs.addr_num = cnt;
57         getaddrs.addrs = (struct sockaddr *)malloc(len);
58         if (NULL == getaddrs.addrs)
59                 return -1;
60
61         len = sizeof(getaddrs);
62         err = getsockopt(sd, SOL_SCTP, optname_old, &getaddrs, &len);
63         if (err < 0) {
64                 free(getaddrs.addrs);
65                 return -1;
66         }
67
68         *addrs = getaddrs.addrs;
69
70         return getaddrs.addr_num;
71
72 } /* sctp_getaddrs_old() */
73
74 /* 
75  * Common getsockopt() layer 
76  * If the NEW getsockopt() API fails this function will fall back to using
77  * the old API
78  */
79 static int
80 sctp_getaddrs(int sd, sctp_assoc_t id,
81               int optname_new, int optname_num_old, int optname_old,
82               struct sockaddr **addrs)
83 {
84         int cnt, err;
85         socklen_t len;
86         size_t bufsize = 4096; /*enough for most cases*/
87
88         struct sctp_getaddrs *getaddrs = (struct sctp_getaddrs*)malloc(bufsize);
89         if(!getaddrs)
90                 return -1;
91         
92         for(;;) {
93                 char *new_buf;
94
95                 len = bufsize;
96                 getaddrs->assoc_id = id;
97                 err = getsockopt(sd, SOL_SCTP, optname_new, getaddrs, &len);
98                 if (err == 0) {
99                         /*got it*/
100                         break;
101                 }
102                 if (errno == ENOPROTOOPT) {
103                         /*Kernel does not support new API*/
104                         free(getaddrs);
105                         return sctp_getaddrs_old(sd, id,
106                                                  optname_num_old, optname_old,
107                                                  addrs);
108                 }
109                 if (errno != ENOMEM ) {
110                         /*unknown error*/
111                         free(getaddrs);
112                         return -1;
113                 }
114                 /*expand buffer*/
115                 if (bufsize > 128*1024) {
116                         /*this is getting ridiculous*/
117                         free(getaddrs);
118                         errno = ENOBUFS;
119                         return -1;
120                 }
121                 new_buf = realloc(getaddrs, bufsize+4096);
122                 if (!new_buf) {
123                         free(getaddrs);
124                         return -1;
125                 }
126                 bufsize += 4096;
127                 getaddrs = (struct sctp_getaddrs*)new_buf;
128         }
129
130         /* we skip traversing the list, allocating a new buffer etc. and enjoy
131          * a simple hack*/
132         cnt = getaddrs->addr_num;
133         memmove(getaddrs, getaddrs + 1, len);
134         *addrs = (struct sockaddr*)getaddrs;
135
136         return cnt;
137 } /* sctp_getaddrs() */
138
139 /* Get all peer address on a socket.  This is a new SCTP API
140  * described in the section 8.3 of the Sockets API Extensions for SCTP.
141  * This is implemented using the getsockopt() interface.
142  */
143 int
144 sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **addrs)
145 {
146         return sctp_getaddrs(sd, id,
147                              SCTP_GET_PEER_ADDRS,
148                              SCTP_GET_PEER_ADDRS_NUM_OLD,
149                              SCTP_GET_PEER_ADDRS_OLD,
150                              addrs);
151 } /* sctp_getpaddrs() */
152
153 /* Frees all resources allocated by sctp_getpaddrs().  This is a new SCTP API
154  * described in the section 8.4 of the Sockets API Extensions for SCTP.
155  */
156 int
157 sctp_freepaddrs(struct sockaddr *addrs)
158 {
159         free(addrs);
160         return 0;
161
162 } /* sctp_freepaddrs() */
163
164 /* Get all locally bound address on a socket.  This is a new SCTP API
165  * described in the section 8.5 of the Sockets API Extensions for SCTP.
166  * This is implemented using the getsockopt() interface.
167  */
168 int
169 sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **addrs)
170 {
171         return sctp_getaddrs(sd, id,
172                              SCTP_GET_LOCAL_ADDRS,
173                              SCTP_GET_LOCAL_ADDRS_NUM_OLD,
174                              SCTP_GET_LOCAL_ADDRS_OLD,
175                              addrs);
176 } /* sctp_getladdrs() */
177
178 /* Frees all resources allocated by sctp_getladdrs().  This is a new SCTP API
179  * described in the section 8.6 of the Sockets API Extensions for SCTP.
180  */
181 int
182 sctp_freeladdrs(struct sockaddr *addrs)
183 {
184         free(addrs);
185         return 0;
186
187 } /* sctp_freeladdrs() */
188
189 int
190 sctp_getaddrlen(sa_family_t family)
191 {
192         /* We could call into the kernel to see what it thinks the size should
193          * be, but hardcoding the address families here is: (a) faster,
194          * (b) easier, and (c) probably good enough for forseeable future.
195          */
196         switch(family) {
197         case AF_INET:
198                 return sizeof(struct sockaddr_in);
199         case AF_INET6:
200                 return sizeof(struct sockaddr_in6);
201         default:
202                 /* Currently there is no defined error handling in
203                  * draft-ietf-tsvwg-sctpsocket-13.txt.
204                  * -1 might cause the application to overwrite buffer
205                  * or misinterpret data. 0 is more likely to cause
206                  * an endless loop.
207                  */
208                 return 0;
209         }
210 }