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;
"Welcome to our mercurial repository"