xref: /linux/fs/smb/client/netmisc.c (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *
4  *   Copyright (c) International Business Machines  Corp., 2002,2008
5  *   Author(s): Steve French (sfrench@us.ibm.com)
6  *
7  *   Error mapping routines from Samba libsmb/errormap.c
8  *   Copyright (C) Andrew Tridgell 2001
9  */
10 
11 #include <linux/net.h>
12 #include <linux/string.h>
13 #include <linux/in.h>
14 #include <linux/ctype.h>
15 #include <linux/fs.h>
16 #include <asm/div64.h>
17 #include <asm/byteorder.h>
18 #include <linux/inet.h>
19 #include "cifsfs.h"
20 #include "cifsglob.h"
21 #include "cifsproto.h"
22 #include "smb1proto.h"
23 #include "smberr.h"
24 #include "cifs_debug.h"
25 #include "nterr.h"
26 
27 /*
28  * Convert a string containing text IPv4 or IPv6 address to binary form.
29  *
30  * Returns 0 on failure.
31  */
32 static int
33 cifs_inet_pton(const int address_family, const char *cp, int len, void *dst)
34 {
35 	int ret = 0;
36 
37 	/* calculate length by finding first slash or NULL */
38 	if (address_family == AF_INET)
39 		ret = in4_pton(cp, len, dst, '\\', NULL);
40 	else if (address_family == AF_INET6)
41 		ret = in6_pton(cp, len, dst , '\\', NULL);
42 
43 	cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n",
44 		 ret, len, len, cp);
45 	if (ret > 0)
46 		ret = 1;
47 	return ret;
48 }
49 
50 /*
51  * Try to convert a string to an IPv4 address and then attempt to convert
52  * it to an IPv6 address if that fails. Set the family field if either
53  * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to
54  * treat the part following it as a numeric sin6_scope_id.
55  *
56  * Returns 0 on failure.
57  */
58 int
59 cifs_convert_address(struct sockaddr *dst, const char *src, int len)
60 {
61 	int rc, alen, slen;
62 	const char *pct;
63 	char scope_id[13];
64 	struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
65 	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
66 
67 	/* IPv4 address */
68 	if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) {
69 		s4->sin_family = AF_INET;
70 		return 1;
71 	}
72 
73 	/* attempt to exclude the scope ID from the address part */
74 	pct = memchr(src, '%', len);
75 	alen = pct ? pct - src : len;
76 
77 	rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr);
78 	if (!rc)
79 		return rc;
80 
81 	s6->sin6_family = AF_INET6;
82 	if (pct) {
83 		/* grab the scope ID */
84 		slen = len - (alen + 1);
85 		if (slen <= 0 || slen > 12)
86 			return 0;
87 		memcpy(scope_id, pct + 1, slen);
88 		scope_id[slen] = '\0';
89 
90 		rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id);
91 		rc = (rc == 0) ? 1 : 0;
92 	}
93 
94 	return rc;
95 }
96 
97 void
98 cifs_set_port(struct sockaddr *addr, const unsigned short int port)
99 {
100 	switch (addr->sa_family) {
101 	case AF_INET:
102 		((struct sockaddr_in *)addr)->sin_port = htons(port);
103 		break;
104 	case AF_INET6:
105 		((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
106 		break;
107 	}
108 }
109 
110 /* The following are taken from fs/ntfs/util.c */
111 
112 #define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
113 
114 /*
115  * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
116  * into Unix UTC (based 1970-01-01, in seconds).
117  */
118 struct timespec64
119 cifs_NTtimeToUnix(__le64 ntutc)
120 {
121 	struct timespec64 ts;
122 	/* BB what about the timezone? BB */
123 
124 	/* Subtract the NTFS time offset, then convert to 1s intervals. */
125 	s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
126 	u64 abs_t;
127 
128 	/*
129 	 * Unfortunately can not use normal 64 bit division on 32 bit arch, but
130 	 * the alternative, do_div, does not work with negative numbers so have
131 	 * to special case them
132 	 */
133 	if (t < 0) {
134 		abs_t = -t;
135 		ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100);
136 		ts.tv_nsec = -ts.tv_nsec;
137 		ts.tv_sec = -abs_t;
138 	} else {
139 		abs_t = t;
140 		ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100;
141 		ts.tv_sec = abs_t;
142 	}
143 
144 	return ts;
145 }
146 
147 /* Convert the Unix UTC into NT UTC. */
148 u64
149 cifs_UnixTimeToNT(struct timespec64 t)
150 {
151 	/* Convert to 100ns intervals and then add the NTFS time offset. */
152 	return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
153 }
154 
155 static const int total_days_of_prev_months[] = {
156 	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
157 };
158 
159 struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
160 {
161 	struct timespec64 ts;
162 	time64_t sec, days;
163 	int min, day, month, year;
164 	u16 date = le16_to_cpu(le_date);
165 	u16 time = le16_to_cpu(le_time);
166 	SMB_TIME *st = (SMB_TIME *)&time;
167 	SMB_DATE *sd = (SMB_DATE *)&date;
168 
169 	cifs_dbg(FYI, "date %d time %d\n", date, time);
170 
171 	sec = 2 * st->TwoSeconds;
172 	min = st->Minutes;
173 	if ((sec > 59) || (min > 59))
174 		cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec);
175 	sec += (min * 60);
176 	sec += 60 * 60 * st->Hours;
177 	if (st->Hours > 24)
178 		cifs_dbg(VFS, "Invalid hours %d\n", st->Hours);
179 	day = sd->Day;
180 	month = sd->Month;
181 	if (day < 1 || day > 31 || month < 1 || month > 12) {
182 		cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day);
183 		day = clamp(day, 1, 31);
184 		month = clamp(month, 1, 12);
185 	}
186 	month -= 1;
187 	days = day + total_days_of_prev_months[month];
188 	days += 3652; /* account for difference in days between 1980 and 1970 */
189 	year = sd->Year;
190 	days += year * 365;
191 	days += (year/4); /* leap year */
192 	/* generalized leap year calculation is more complex, ie no leap year
193 	for years/100 except for years/400, but since the maximum number for DOS
194 	 year is 2**7, the last year is 1980+127, which means we need only
195 	 consider 2 special case years, ie the years 2000 and 2100, and only
196 	 adjust for the lack of leap year for the year 2100, as 2000 was a
197 	 leap year (divisible by 400) */
198 	if (year >= 120)  /* the year 2100 */
199 		days = days - 1;  /* do not count leap year for the year 2100 */
200 
201 	/* adjust for leap year where we are still before leap day */
202 	if (year != 120)
203 		days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0);
204 	sec += 24 * 60 * 60 * days;
205 
206 	ts.tv_sec = sec + offset;
207 
208 	/* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */
209 
210 	ts.tv_nsec = 0;
211 	return ts;
212 }
213