aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm/apn.c
blob: 88b45a4bf832badeb19c3ba637ca27df9790621d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*! \file apn.c 
 *
 * (C) 2014 by Harald Welte <laforge@gnumonks.org>
 * (C) 2015,2017 by sysmocom - s.f.m.c. GmbH
 * All Rights Reserved.
 *
 * SPDX-License-Identifier: GPL-2.0+
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <talloc.h>

#include <osmocom/gsm/apn.h>

#define APN_OI_GPRS_FMT	"mnc%03u.mcc%03u.gprs"
#define APN_GPRS_FMT	"%s.mnc%03u.mcc%03u.gprs"

static char apn_strbuf[APN_MAXLEN+1];

char *osmo_apn_qualify_buf(char *buf, size_t buf_len, unsigned int mcc, unsigned int mnc, const char *ni)
{
	snprintf(buf, buf_len-1, APN_GPRS_FMT, ni, mnc, mcc);
	buf[buf_len-1] = '\0';

	return buf;
}

char *osmo_apn_qualify(unsigned int mcc, unsigned int mnc, const char *ni)
{
	return osmo_apn_qualify_buf(apn_strbuf, sizeof(apn_strbuf), mcc, mnc, ni);
}

char *osmo_apn_qualify_c(const void *ctx, unsigned int mcc, unsigned int mnc, const char *ni)
{
	char *buf = talloc_size(ctx, APN_MAXLEN);
	if (!buf)
		return NULL;
	return osmo_apn_qualify_buf(buf, APN_MAXLEN, mcc, mnc, ni);
}

char *osmo_apn_qualify_from_imsi_buf(char *buf, size_t buf_len, const char *imsi,
				     const char *ni, int have_3dig_mnc)
{
	char cbuf[3+1], nbuf[3+1];

	strncpy(cbuf, imsi, 3);
	cbuf[3] = '\0';

	if (have_3dig_mnc) {
		strncpy(nbuf, imsi+3, 3);
		nbuf[3] = '\0';
	} else {
		strncpy(nbuf, imsi+3, 2);
		nbuf[2] = '\0';
	}
	return osmo_apn_qualify_buf(buf, buf_len, atoi(cbuf), atoi(nbuf), ni);
}

char *osmo_apn_qualify_from_imsi(const char *imsi,
				 const char *ni, int have_3dig_mnc)
{
	return osmo_apn_qualify_from_imsi_buf(apn_strbuf, sizeof(apn_strbuf), imsi, ni, have_3dig_mnc);
}

char *osmo_apn_qualify_from_imsi_c(const void *ctx, const char *imsi, const char *ni, int have_3dig_mnc)
{
	char *buf = talloc_size(ctx, APN_MAXLEN);
	if (!buf)
		return NULL;
	return osmo_apn_qualify_from_imsi_buf(buf, APN_MAXLEN, imsi, ni, have_3dig_mnc);
}

/**
 * Convert an encoded APN into a dot-separated string.
 *
 * \param out_str      the destination buffer (size must be >= max(app_enc_len,1))
 * \param apn_enc      the encoded APN
 * \param apn_enc_len  the length of the encoded APN
 *
 * \returns out_str on success and NULL otherwise
 */
char *osmo_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t apn_enc_len)
{
	char *str = out_str;
	size_t rest_chars = apn_enc_len;

	if (!apn_enc)
		return NULL;

	while (rest_chars > 0 && apn_enc[0]) {
		size_t label_size = apn_enc[0];
		if (label_size + 1 > rest_chars)
			return NULL;

		memmove(str, apn_enc + 1, label_size);
		str += label_size;
		rest_chars -= label_size + 1;
		apn_enc += label_size + 1;

		if (rest_chars)
			*(str++) = '.';
	}
	str[0] = '\0';

	return out_str;
}

/**
 * Convert a dot-separated string into an encoded APN.
 *
 * \param apn_enc          the encoded APN
 * \param max_apn_enc_len  the size of the apn_enc buffer
 * \param str              the source string
 *
 * \returns out_str on success and NULL otherwise
 */
int osmo_apn_from_str(uint8_t *apn_enc, size_t max_apn_enc_len, const char *str)
{
	uint8_t *last_len_field;
	int len;

	/* Can we even write the length field to the output? */
	if (max_apn_enc_len == 0)
		return -1;

	/* Remember where we need to put the length once we know it */
	last_len_field = apn_enc;
	len = 1;
	apn_enc += 1;

	while (str[0]) {
		if (len >= max_apn_enc_len)
			return -1;

		if (str[0] == '.') {
			*last_len_field = (apn_enc - last_len_field) - 1;
			last_len_field = apn_enc;
		} else {
			*apn_enc = str[0];
		}
		apn_enc += 1;
		str += 1;
		len += 1;
	}

	*last_len_field = (apn_enc - last_len_field) - 1;

	return len;
}