Download
vkoskiv
/MacNTP
/main.c
(View History)
vkoskiv Some touchups for initial v0.1 release | Latest amendment: 20 on 2024-01-21 |
1 | /* |
2 | * Copyright (c) 2023 Valtteri Koskivuori <vkoskiv@gmail.com> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | /* |
18 | TODO: |
19 | - Display possible errors in cdev |
20 | */ |
21 | |
22 | #include <SetUpA4.h> |
23 | #include <GestaltEqu.h> |
24 | #include "MacNTP.h" |
25 | #include "ShowInitIcon.h" |
26 | #include "rsrcid.h" |
27 | |
28 | // This enables DebugStr() printouts to aid development. |
29 | // This also disables the system time check, so it tries NTP |
30 | // on every boot. |
31 | //#define DEBUGGING |
32 | |
33 | void dump_error(enum MacNTPError error, OSErr oserror); |
34 | int TryMacNTP(Str255 ntp1, Str255 ntp2, Str255 utc); |
35 | int is_number(char c); |
36 | int validate_utc(Str255 utc_str); |
37 | int utcstr_to_mins(Str255 utc, long *out); |
38 | |
39 | #define SECONDS_TO_02_09_1996 2924510400ul |
40 | |
41 | Handle g_mtcp_init_handle; |
42 | |
43 | void dump_error(enum MacNTPError error, OSErr oserror) { |
44 | // Str255 numstr; |
45 | StringHandle err_str; |
46 | err_str = GetString(ERR_STR_ID); |
47 | if (!err_str) { |
48 | return; |
49 | } |
50 | SetHandleSize(err_str, 256); |
51 | HLock(err_str); |
52 | // NumToString(oserror, numstr); |
53 | switch (error) { |
54 | case Success: |
55 | SetString(err_str, "\p"); //FIXME: Bit dumb to write this every time? |
56 | break; |
57 | case MacTCPInitFailed: |
58 | SetString(err_str, "\pMacTCP init failed"); |
59 | break; |
60 | case MacTCPNotFound: |
61 | SetString(err_str, "\pMacTCP not found"); |
62 | break; |
63 | case InvalidURL: |
64 | SetString(err_str, "\pInvalid URL"); |
65 | break; |
66 | case BadNtpStructSize: |
67 | SetString(err_str, "\pBad NTP struct size"); |
68 | break; |
69 | case UDPCreateFailed: |
70 | SetString(err_str, "\pUDPCreate failed"); |
71 | break; |
72 | case UDPSendFailed: |
73 | SetString(err_str, "\pUDPSend failed"); |
74 | break; |
75 | case UDPRcvFailed: |
76 | SetString(err_str, "\pUDPRcv failed"); |
77 | break; |
78 | case InvalidNTPResponse: |
79 | SetString(err_str, "\pInvalid NTP response"); |
80 | break; |
81 | case OriginTimestampMismatch: |
82 | SetString(err_str, "\pOrigin timestamp mismatch"); |
83 | break; |
84 | case DNSResolveFailed: |
85 | SetString(err_str, "\pDNS resolve failed"); |
86 | break; |
87 | case GestaltMissing: |
88 | SetString(err_str, "\pMissing Gestalt, MacNTP requires >=6.0.8"); |
89 | break; |
90 | case ClockWriteFailed: |
91 | SetString(err_str, "\pClock write failed"); |
92 | break; |
93 | case ClockReadFailed: |
94 | SetString(err_str, "\pClock read failed"); |
95 | break; |
96 | case UDPRcvTimedOut: |
97 | SetString(err_str, "\pUDPRcv timed out"); |
98 | break; |
99 | case PacketParamNull: |
100 | SetString(err_str, "\pPacket param null"); |
101 | break; |
102 | } |
103 | HUnlock(err_str); |
104 | ChangedResource(err_str); |
105 | ReleaseResource(err_str); |
106 | UpdateResFile(CurResFile()); |
107 | } |
108 | |
109 | //FIXME: This is duplicated from cdev.c, but I didn't want to |
110 | //make a library and all the extra build steps just for this. |
111 | //Maybe we'll just stick it in MacNTPLib and figure out linkage somehow |
112 | int is_number(char c) { |
113 | return c >> 4 == 3 && (c & 0x0F) < 0xA; |
114 | } |
115 | |
116 | int validate_utc(Str255 utc_str) { |
117 | // Str255 is a pointer to a pascal string, where |
118 | // the first byte is the string length. |
119 | char scratch[6]; |
120 | short strlen; |
121 | char *str; |
122 | strlen = utc_str[0]; |
123 | str = (char *)&utc_str[1]; |
124 | if (strlen != 6) return false; |
125 | if (str[0] != '+' && str[0] != '-') return false; |
126 | if (!is_number(str[1]) || !is_number(str[2])) return false; |
127 | if (str[3] != ':') return false; |
128 | if (!is_number(str[4]) || !is_number(str[5])) return false; |
129 | |
130 | return true; |
131 | } |
132 | int utcstr_to_mins(Str255 utc, long *out) { |
133 | long minutes; |
134 | if (!validate_utc(utc)) return false; |
135 | minutes = 0; |
136 | minutes += ((utc[2] - 0x30) * 10) * 60; |
137 | minutes += (utc[3] - 0x30) * 60; |
138 | minutes += (utc[5] - 0x30) * 10; |
139 | minutes += (utc[6] - 0x30); |
140 | if (utc[1] == '-') |
141 | minutes = -minutes; |
142 | if (out) *out = minutes; |
143 | return true; |
144 | } |
145 | |
146 | int TryMacNTP(Str255 ntp1, Str255 ntp2, Str255 utc) { |
147 | struct ntp_packet payload; |
148 | enum MacNTPError error; |
149 | OSErr oserr = 0; |
150 | minutes_t utc_offset; |
151 | |
152 | if (!utc[0]) return 1; |
153 | if (!utcstr_to_mins(utc, &utc_offset)) return 1; |
154 | |
155 | if (!ntp1[0]) return 1; |
156 | PtoCstr(ntp1); |
157 | error = MacNTPFetchTime((char *)(ntp1), &payload, &oserr, utc_offset); |
158 | // If the first lookup fails and we have a fallback, try it. |
159 | if (error == DNSResolveFailed && ntp2[0]) { |
160 | PtoCstr(ntp2); |
161 | error = MacNTPFetchTime((char *)(ntp2), &payload, &oserr, utc_offset); |
162 | } |
163 | dump_error(error, oserr); |
164 | if (error) return 1; |
165 | |
166 | error = MacNTPSetSystemTime(&payload, utc_offset); |
167 | dump_error(error, oserr); |
168 | if (error) return 1; |
169 | return 0; |
170 | } |
171 | |
172 | void main() { |
173 | Ptr our_init_ptr; |
174 | unsigned long systime; |
175 | StringHandle ntp1; |
176 | StringHandle ntp2; |
177 | StringHandle utc; // Offset in minutes as pstr |
178 | long answer; |
179 | OSErr gestalt_err; |
180 | asm { |
181 | move.L A0, our_init_ptr; |
182 | } |
183 | RememberA0(); |
184 | SetUpA4(); |
185 | // This is already locked in the resource attrs |
186 | g_mtcp_init_handle = RecoverHandle(our_init_ptr); |
187 | |
188 | // First we check if MacTCP is available |
189 | // This also assumes Gestalt support |
190 | gestalt_err = Gestalt(gestaltVersion, &answer); |
191 | if (gestalt_err) { |
192 | // Gestalt not available, it was introduced in 6.0.4 |
193 | // And MacTCP in 6.0.3, so we're just going to assume |
194 | // the user has at least 6.0.8 |
195 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
196 | dump_error(GestaltMissing, 0); |
197 | goto skip; |
198 | } |
199 | |
200 | if (Gestalt('mtcp', nil)) { |
201 | // MacTCP not found |
202 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
203 | dump_error(MacTCPNotFound, 0); |
204 | goto skip; |
205 | } |
206 | |
207 | ntp1 = GetString(NTP1_STR_ID); |
208 | ntp2 = GetString(NTP2_STR_ID); |
209 | utc = GetString(UTC_STR_ID); |
210 | if (!ntp1 || !ntp2 || !utc) { |
211 | #ifdef DEBUGGING |
212 | DebugStr("\pSTR rsrcs missing"); |
213 | #endif |
214 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
215 | goto skip; |
216 | } |
217 | /*if (!*ntp1[0] && !*ntp2[0]) { |
218 | #ifdef DEBUGGING |
219 | DebugStr("\pBoth NTP urls empty"); |
220 | #endif |
221 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
222 | goto skip; |
223 | }*/ |
224 | HLock(ntp1); |
225 | HLock(ntp2); |
226 | HLock(utc); |
227 | |
228 | //TODO: Move this up a bit |
229 | #ifndef DEBUGGING |
230 | // We only try to update the clock if we're well into the 20th century |
231 | ReadDateTime((unsigned long *)&systime); |
232 | if (systime > SECONDS_TO_02_09_1996) { |
233 | ShowInitIcon(MACNTP_ICN_ID_SUCCESS, true); |
234 | goto skip; |
235 | } else { |
236 | ShowInitIcon(MACNTP_ICN_ID_PENDING, false); |
237 | } |
238 | #else |
239 | ShowInitIcon(MACNTP_ICN_ID_PENDING, false); |
240 | #endif |
241 | |
242 | if (!TryMacNTP(*ntp1, *ntp2, *utc)) { |
243 | ShowInitIcon(MACNTP_ICN_ID_SUCCESS, true); |
244 | } else { |
245 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
246 | } |
247 | |
248 | // I have no idea if unlock is needed here. Maybe not. |
249 | HUnlock(ntp1); |
250 | //DisposHandle(ntp1); |
251 | HUnlock(ntp2); |
252 | // For whatever reason, System 6 crashes on boot if we |
253 | // try to dispose this specific handle. Okay. |
254 | //DisposHandle(ntp2); |
255 | HUnlock(utc); |
256 | //DisposHandle(utc); |
257 | HUnlock(g_mtcp_init_handle); |
258 | skip: |
259 | error: |
260 | RestoreA4(); |
261 | } |