Mercurial > hg > freeDiameter
comparison extensions/app_radgw/rgw_msg.c @ 356:e203fc0c95e3
Updated the app_radgw extension to allow more souple management of sessions, and stateful gateway features.
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Thu, 01 Jul 2010 15:47:34 +0900 |
parents | 50bfb29bf036 |
children | 1c2f5ee38039 |
comparison
equal
deleted
inserted
replaced
355:1ae3f2c28737 | 356:e203fc0c95e3 |
---|---|
134 /* If we need to dump the value, it's better to call directly radius_msg_dump instead... */ | 134 /* If we need to dump the value, it's better to call directly radius_msg_dump instead... */ |
135 } | 135 } |
136 fd_log_debug("-----------------------------\n"); | 136 fd_log_debug("-----------------------------\n"); |
137 } | 137 } |
138 | 138 |
139 static struct dict_object * cache_sess_id = NULL; | |
140 static struct dict_object * cache_dest_host = NULL; | |
141 static struct dict_object * cache_dest_realm = NULL; | |
142 static struct dict_object * cache_orig_host = NULL; | 139 static struct dict_object * cache_orig_host = NULL; |
143 static struct dict_object * cache_orig_realm = NULL; | 140 static struct dict_object * cache_orig_realm = NULL; |
144 | 141 |
145 int rgw_msg_init(void) | 142 int rgw_msg_init(void) |
146 { | 143 { |
147 TRACE_ENTRY(); | 144 TRACE_ENTRY(); |
148 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &cache_sess_id, ENOENT) ); | |
149 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &cache_dest_host, ENOENT) ); | |
150 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &cache_dest_realm, ENOENT) ); | |
151 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); | 145 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); |
152 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); | 146 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); |
153 return 0; | 147 return 0; |
154 } | 148 } |
155 | 149 |
156 /* Create a msg with origin-host & realm, and session-id, and a session object from a RADIUS request message */ | 150 /* Create a new Diameter msg with origin-host & realm */ |
157 int rgw_msg_create_base(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, struct session ** session, struct msg ** diam) | 151 int rgw_msg_create_base(struct rgw_client * cli, struct msg ** diam) |
158 { | 152 { |
159 int idx, i; | |
160 const char * prefix = "Diameter/"; | |
161 size_t pref_len; | |
162 char * dh = NULL; | |
163 size_t dh_len = 0; | |
164 char * dr = NULL; | |
165 size_t dr_len = 0; | |
166 char * si = NULL; | |
167 size_t si_len = 0; | |
168 char * un = NULL; | |
169 size_t un_len = 0; | |
170 | |
171 char * fqdn; | 153 char * fqdn; |
172 char * realm; | 154 char * realm; |
173 char * sess_str = NULL; | |
174 | 155 |
175 struct avp *avp = NULL; | 156 struct avp *avp = NULL; |
176 union avp_value avp_val; | 157 union avp_value avp_val; |
177 | 158 |
178 TRACE_ENTRY("%p %p %p %p", msg, cli, session, diam); | 159 TRACE_ENTRY("%p %p", cli, diam); |
179 CHECK_PARAMS( msg && cli && session && (*session == NULL) && diam && (*diam == NULL) ); | 160 CHECK_PARAMS( cli && diam && (*diam == NULL) ); |
180 | |
181 pref_len = strlen(prefix); | |
182 | |
183 /* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Destination-Host/Destination-Realm/Session-Id) */ | |
184 /* NOTE: RFC4005 says "Origin-Host" here, but it's not coherent with the rules for answers. Destination-Host makes more sense */ | |
185 /* Is there a Class attribute with prefix "Diameter/" in the message? (in that case: Diameter/Session-Id) */ | |
186 for (idx = 0; idx < msg->radius.attr_used; idx++) { | |
187 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); | |
188 char * attr_val = (char *)(attr + 1); | |
189 size_t attr_len = attr->length - sizeof(struct radius_attr_hdr); | |
190 | |
191 if ((attr->type == RADIUS_ATTR_USER_NAME) | |
192 && attr_len) { | |
193 TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr_len, attr_val); | |
194 un = attr_val; | |
195 un_len = attr_len; | |
196 continue; | |
197 } | |
198 | |
199 if ((attr->type == RADIUS_ATTR_STATE) | |
200 && (attr_len > pref_len + 5 /* for the '/'s and non empty strings */ ) | |
201 && ! strncmp(attr_val, prefix, pref_len)) { /* should we make it strncasecmp? */ | |
202 int i, start; | |
203 | |
204 TRACE_DEBUG(ANNOYING, "Found a State attribute with '%s' prefix (attr #%d).", prefix, idx); | |
205 | |
206 /* Now parse the value and check its content is valid. Unfortunately we cannot use strchr here since strings are not \0-terminated */ | |
207 | |
208 i = start = pref_len; | |
209 dh = attr_val + i; | |
210 for (; (i < attr_len - 2) && (attr_val[i] != '/'); i++) /* loop */; | |
211 if ( i >= attr_len - 2 ) continue; /* the attribute format is not good */ | |
212 dh_len = i - start; | |
213 | |
214 start = ++i; | |
215 dr = attr_val + i; | |
216 for (; (i < attr_len - 1) && (attr_val[i] != '/'); i++) /* loop */; | |
217 if ( i >= attr_len - 1 ) continue; /* the attribute format is not good */ | |
218 dr_len = i - start; | |
219 | |
220 i++; | |
221 si = attr_val + i; | |
222 si_len = attr_len - i; | |
223 | |
224 TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", dh_len, dh, dr_len, dr, si_len, si); | |
225 /* Remove from the message */ | |
226 for (i = idx + 1; i < msg->radius.attr_used; i++) | |
227 msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i]; | |
228 msg->radius.attr_used -= 1; | |
229 break; | |
230 } | |
231 | |
232 if ((attr->type == RADIUS_ATTR_CLASS) | |
233 && (attr_len > pref_len ) | |
234 && ! strncmp(attr_val, prefix, pref_len)) { | |
235 si = attr_val + pref_len; | |
236 si_len = attr_len - pref_len; | |
237 TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), SI:'%.*s'.", prefix, idx, si_len, si); | |
238 /* Remove from the message */ | |
239 for (i = idx + 1; i < msg->radius.attr_used; i++) | |
240 msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i]; | |
241 msg->radius.attr_used -= 1; | |
242 break; | |
243 } | |
244 | |
245 } | |
246 | 161 |
247 /* Get information on this peer */ | 162 /* Get information on this peer */ |
248 CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); | 163 CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); |
249 | 164 |
250 /* Create the session object */ | |
251 if (si_len) { | |
252 CHECK_FCT( fd_sess_fromsid ( si, si_len, session, &idx) ); | |
253 } else { | |
254 if (un) { | |
255 int len; | |
256 /* If not found, create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */ | |
257 CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) ); | |
258 len = sprintf(sess_str, "%.*s;%s", un_len, un, fd_g_config->cnf_diamid); | |
259 CHECK_FCT( fd_sess_new(session, fqdn, sess_str, len) ); | |
260 free(sess_str); | |
261 idx = 1; | |
262 } | |
263 } | |
264 | |
265 /* Create an empty Diameter message so that extensions can store their AVPs */ | 165 /* Create an empty Diameter message so that extensions can store their AVPs */ |
266 CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); | 166 CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); |
267 | |
268 if (*session) { | |
269 CHECK_FCT( fd_sess_getsid(*session, &sess_str) ); | |
270 if (idx == 0) { | |
271 TRACE_DEBUG(INFO, "Another message was translated for this session ('%s') and not answered yet, discarding the new RADIUS request.", sess_str); | |
272 *session = NULL; | |
273 return EALREADY; | |
274 } | |
275 | |
276 TRACE_DEBUG(FULL, "Translating new message for session '%s'...", sess_str); | |
277 | |
278 /* Add the Session-Id AVP as first AVP */ | |
279 CHECK_FCT( fd_msg_avp_new ( cache_sess_id, 0, &avp ) ); | |
280 memset(&avp_val, 0, sizeof(avp_val)); | |
281 avp_val.os.data = (unsigned char *)sess_str; | |
282 avp_val.os.len = strlen(sess_str); | |
283 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
284 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_FIRST_CHILD, avp) ); | |
285 | |
286 } else { | |
287 TRACE_DEBUG(FULL, "No session has been created for this message"); | |
288 } | |
289 | |
290 /* Add the Destination-Realm as next AVP */ | |
291 CHECK_FCT( fd_msg_avp_new ( cache_dest_realm, 0, &avp ) ); | |
292 memset(&avp_val, 0, sizeof(avp_val)); | |
293 if (dr) { | |
294 avp_val.os.data = (unsigned char *)dr; | |
295 avp_val.os.len = dr_len; | |
296 } else { | |
297 int i = 0; | |
298 if (un) { | |
299 /* Is there an '@' in the user name? We don't care for decorated NAI here */ | |
300 for (i = un_len - 2; i > 0; i--) { | |
301 if (un[i] == '@') { | |
302 i++; | |
303 break; | |
304 } | |
305 } | |
306 } | |
307 if (i == 0) { | |
308 /* Not found in the User-Name => we use the local domain of this gateway */ | |
309 avp_val.os.data = fd_g_config->cnf_diamrlm; | |
310 avp_val.os.len = fd_g_config->cnf_diamrlm_len; | |
311 } else { | |
312 avp_val.os.data = un + i; | |
313 avp_val.os.len = un_len - i; | |
314 } | |
315 } | |
316 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
317 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); | |
318 | |
319 /* Add the Destination-Host as next AVP */ | |
320 if (dh) { | |
321 CHECK_FCT( fd_msg_avp_new ( cache_dest_host, 0, &avp ) ); | |
322 memset(&avp_val, 0, sizeof(avp_val)); | |
323 avp_val.os.data = (unsigned char *)dh; | |
324 avp_val.os.len = dh_len; | |
325 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); | |
326 CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); | |
327 } | |
328 | 167 |
329 /* Add the Origin-Host as next AVP */ | 168 /* Add the Origin-Host as next AVP */ |
330 CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); | 169 CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); |
331 memset(&avp_val, 0, sizeof(avp_val)); | 170 memset(&avp_val, 0, sizeof(avp_val)); |
332 avp_val.os.data = (unsigned char *)fqdn; | 171 avp_val.os.data = (unsigned char *)fqdn; |