blob: cae73f2939875e05f1ac87b4f47998f28571d785 [file] [log] [blame]
Oliver Smithbf7deda2019-11-20 10:56:35 +01001/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <osmocom/hlr/logging.h>
20#include <osmocom/mslookup/mslookup_client.h>
21#include <osmocom/mslookup/mslookup_client_fake.h>
22
23#include <string.h>
24
25/* Fake mslookup method */
26
27struct fake_lookup_state {
28 struct osmo_mslookup_client *client;
29 struct llist_head requests;
30 struct osmo_timer_list async_response_timer;
31 struct osmo_mslookup_fake_response *responses;
32 size_t responses_len;
33};
34
35struct fake_lookup_request {
36 struct llist_head entry;
37 uint32_t request_handle;
38 struct osmo_mslookup_query query;
39 struct timeval received_at;
40};
41
42/*! Args for osmo_timer_schedule: seconds and microseconds. */
43#define ASYNC_RESPONSE_PERIOD 0, (1e6 / 10)
44static void fake_lookup_async_response(void *state);
45
46static void fake_lookup_request(struct osmo_mslookup_client_method *method,
47 const struct osmo_mslookup_query *query,
48 uint32_t request_handle)
49{
50 struct fake_lookup_state *state = method->priv;
51 char buf[256];
52 LOGP(DMSLOOKUP, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL));
53
54 /* A real implementation would send packets to some remote server.
55 * Here this is simulated: add to the list of requests, which fake_lookup_async_response() will reply upon
56 * according to the test data listing the replies that the test wants to generate. */
57
58 struct fake_lookup_request *r = talloc_zero(method->client, struct fake_lookup_request);
59 *r = (struct fake_lookup_request){
60 .request_handle = request_handle,
61 .query = *query,
62 };
63 osmo_gettimeofday(&r->received_at, NULL);
64 llist_add_tail(&r->entry, &state->requests);
65}
66
67static void fake_lookup_request_cleanup(struct osmo_mslookup_client_method *method,
68 uint32_t request_handle)
69{
70 struct fake_lookup_state *state = method->priv;
71
72 /* Tear down any state associated with this handle. */
73 struct fake_lookup_request *r;
74 llist_for_each_entry(r, &state->requests, entry) {
75 if (r->request_handle != request_handle)
76 continue;
77 llist_del(&r->entry);
78 talloc_free(r);
79 LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() ok\n", __func__);
80 return;
81 }
82 LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() FAILED\n", __func__);
83}
84
85static void fake_lookup_async_response(void *data)
86{
87 struct fake_lookup_state *state = data;
88 struct fake_lookup_request *req, *n;
89 struct timeval now;
90 char str[256];
91
92 osmo_gettimeofday(&now, NULL);
93
94 llist_for_each_entry_safe(req, n, &state->requests, entry) {
95 struct osmo_mslookup_fake_response *resp;
96
97 for (resp = state->responses;
98 (resp - state->responses) < state->responses_len;
99 resp++) {
100 struct timeval diff;
101
102 if (resp->sent)
103 continue;
104 if (osmo_mslookup_id_cmp(&req->query.id, &resp->for_id) != 0)
105 continue;
106 if (strcmp(req->query.service, resp->for_service) != 0)
107 continue;
108
109 timersub(&now, &req->received_at, &diff);
110 if (timercmp(&diff, &resp->time_to_reply, <))
111 continue;
112
113 /* It's time to reply to this request. */
114 LOGP(DMSLOOKUP, LOGL_DEBUG, "osmo_mslookup_client_rx_result(): %s\n",
115 osmo_mslookup_result_name_b(str, sizeof(str), &req->query, &resp->result));
116 osmo_mslookup_client_rx_result(state->client, req->request_handle, &resp->result);
117 resp->sent = true;
118
119 /* The req will have been cleaned up now, so we must not iterate over state->responses anymore
120 * with this req. */
121 break;
122 }
123 }
124
125 osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);
126}
127
128struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client,
129 struct osmo_mslookup_fake_response *responses,
130 size_t responses_len)
131{
132 struct osmo_mslookup_client_method *method = talloc_zero(client, struct osmo_mslookup_client_method);
133 OSMO_ASSERT(method);
134
135 struct fake_lookup_state *state = talloc_zero(method, struct fake_lookup_state);
136 OSMO_ASSERT(state);
137 *state = (struct fake_lookup_state){
138 .client = client,
139 .responses = responses,
140 .responses_len = responses_len,
141 };
142 INIT_LLIST_HEAD(&state->requests);
143
144 *method = (struct osmo_mslookup_client_method){
145 .name = "fake",
146 .priv = state,
147 .request = fake_lookup_request,
148 .request_cleanup = fake_lookup_request_cleanup,
149 };
150
151 osmo_timer_setup(&state->async_response_timer, fake_lookup_async_response, state);
152 osmo_mslookup_client_method_add(client, method);
153
154 osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD);
155 return method;
156}