Download
vkoskiv
/MacNTP
/main.c
(View History)
vkoskiv Display MacNTP errors in cdev | Latest amendment: 19 on 2023-09-20 |
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 | SysBeep(10); |
49 | SysBeep(1); |
50 | return; |
51 | } |
52 | SetHandleSize(err_str, 256); |
53 | HLock(err_str); |
54 | NumToString(oserror, numstr); |
55 | switch (error) { |
56 | case Success: |
57 | SetString(err_str, "\p"); //FIXME: Bit dumb to write this every time? |
58 | break; |
59 | case MacTCPInitFailed: |
60 | SetString(err_str, "\pMacTCP init failed"); |
61 | break; |
62 | case InvalidURL: |
63 | SetString(err_str, "\pInvalid URL"); |
64 | break; |
65 | case BadNtpStructSize: |
66 | SetString(err_str, "\pBad NTP struct size"); |
67 | break; |
68 | case UDPCreateFailed: |
69 | SetString(err_str, "\pUDPCreate failed"); |
70 | break; |
71 | case UDPSendFailed: |
72 | SetString(err_str, "\pUDPSend failed"); |
73 | break; |
74 | case UDPRcvFailed: |
75 | SetString(err_str, "\pUDPRcv failed"); |
76 | break; |
77 | case InvalidNTPResponse: |
78 | SetString(err_str, "\pInvalid NTP response"); |
79 | break; |
80 | case OriginTimestampMismatch: |
81 | SetString(err_str, "\pOrigin timestamp mismatch"); |
82 | break; |
83 | case DNSResolveFailed: |
84 | SetString(err_str, "\pDNS resolve failed"); |
85 | break; |
86 | case ClockWriteFailed: |
87 | SetString(err_str, "\pClock write failed"); |
88 | break; |
89 | case ClockReadFailed: |
90 | SetString(err_str, "\pClock read failed"); |
91 | break; |
92 | case UDPRcvTimedOut: |
93 | SetString(err_str, "\pUDPRcv timed out"); |
94 | break; |
95 | case PacketParamNull: |
96 | SetString(err_str, "\pPacket param null"); |
97 | break; |
98 | } |
99 | HUnlock(err_str); |
100 | ChangedResource(err_str); |
101 | ReleaseResource(err_str); |
102 | UpdateResFile(CurResFile()); |
103 | } |
104 | |
105 | //FIXME: This is duplicated from cdev.c, but I didn't want to |
106 | //make a library and all the extra build steps just for this. |
107 | //Maybe we'll just stick it in MacNTPLib and figure out linkage somehow |
108 | int is_number(char c) { |
109 | return c >> 4 == 3 && (c & 0x0F) < 0xA; |
110 | } |
111 | |
112 | int validate_utc(Str255 utc_str) { |
113 | // Str255 is a pointer to a pascal string, where |
114 | // the first byte is the string length. |
115 | char scratch[6]; |
116 | short strlen; |
117 | char *str; |
118 | strlen = utc_str[0]; |
119 | str = (char *)&utc_str[1]; |
120 | if (strlen != 6) return false; |
121 | if (str[0] != '+' && str[0] != '-') return false; |
122 | if (!is_number(str[1]) || !is_number(str[2])) return false; |
123 | if (str[3] != ':') return false; |
124 | if (!is_number(str[4]) || !is_number(str[5])) return false; |
125 | |
126 | return true; |
127 | } |
128 | int utcstr_to_mins(Str255 utc, long *out) { |
129 | long minutes; |
130 | if (!validate_utc(utc)) return false; |
131 | minutes = 0; |
132 | minutes += ((utc[2] - 0x30) * 10) * 60; |
133 | minutes += (utc[3] - 0x30) * 60; |
134 | minutes += (utc[5] - 0x30) * 10; |
135 | minutes += (utc[6] - 0x30); |
136 | if (utc[1] == '-') |
137 | minutes = -minutes; |
138 | if (out) *out = minutes; |
139 | return true; |
140 | } |
141 | |
142 | int TryMacNTP(Str255 ntp1, Str255 ntp2, Str255 utc) { |
143 | struct ntp_packet payload; |
144 | enum MacNTPError error; |
145 | OSErr oserr = 0; |
146 | minutes_t utc_offset; |
147 | |
148 | if (!utc[0]) return 1; |
149 | if (!utcstr_to_mins(utc, &utc_offset)) return 1; |
150 | |
151 | if (!ntp1[0]) return 1; |
152 | PtoCstr(ntp1); |
153 | error = MacNTPFetchTime((char *)(ntp1), &payload, &oserr, utc_offset); |
154 | if (error == DNSResolveFailed) SysBeep(1); |
155 | // If the first lookup fails and we have a fallback, try it. |
156 | if (error == DNSResolveFailed && ntp2[0]) { |
157 | PtoCstr(ntp2); |
158 | error = MacNTPFetchTime((char *)(ntp2), &payload, &oserr, utc_offset); |
159 | } |
160 | dump_error(error, oserr); |
161 | if (error) return 1; |
162 | |
163 | error = MacNTPSetSystemTime(&payload, utc_offset); |
164 | dump_error(error, oserr); |
165 | if (error) return 1; |
166 | return 0; |
167 | } |
168 | |
169 | void main() { |
170 | Ptr our_init_ptr; |
171 | unsigned long systime; |
172 | StringHandle ntp1; |
173 | StringHandle ntp2; |
174 | StringHandle utc; // Offset in minutes as pstr |
175 | long answer; |
176 | OSErr gestalt_err; |
177 | asm { |
178 | move.L A0, our_init_ptr; |
179 | } |
180 | RememberA0(); |
181 | SetUpA4(); |
182 | // This is already locked in the resource attrs |
183 | g_mtcp_init_handle = RecoverHandle(our_init_ptr); |
184 | |
185 | // First we check if MacTCP is available |
186 | // This also assumes Gestalt support |
187 | gestalt_err = Gestalt(gestaltVersion, &answer); |
188 | if (gestalt_err) { |
189 | // Gestalt not available, it was introduced in 6.0.4 |
190 | // And MacTCP in 6.0.3, so we're just going to assume |
191 | // the user has at least 6.0.8 |
192 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
193 | goto skip; |
194 | } |
195 | |
196 | if (Gestalt('mtcp', nil)) { |
197 | // MacTCP not found |
198 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
199 | goto skip; |
200 | } |
201 | |
202 | ntp1 = GetString(NTP1_STR_ID); |
203 | ntp2 = GetString(NTP2_STR_ID); |
204 | utc = GetString(UTC_STR_ID); |
205 | if (!ntp1 || !ntp2 || !utc) { |
206 | #ifdef DEBUGGING |
207 | DebugStr("\pSTR rsrcs missing"); |
208 | #endif |
209 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
210 | goto skip; |
211 | } |
212 | /*if (!*ntp1[0] && !*ntp2[0]) { |
213 | #ifdef DEBUGGING |
214 | DebugStr("\pBoth NTP urls empty"); |
215 | #endif |
216 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
217 | goto skip; |
218 | }*/ |
219 | HLock(ntp1); |
220 | HLock(ntp2); |
221 | HLock(utc); |
222 | |
223 | //TODO: Move this up a bit |
224 | #ifndef DEBUGGING |
225 | // We only try to update the clock if we're well into the 20th century |
226 | ReadDateTime((unsigned long *)&systime); |
227 | if (systime > SECONDS_TO_02_09_1996) { |
228 | ShowInitIcon(MACNTP_ICN_ID_SUCCESS, true); |
229 | goto skip; |
230 | } else { |
231 | ShowInitIcon(MACNTP_ICN_ID_PENDING, false); |
232 | } |
233 | #else |
234 | ShowInitIcon(MACNTP_ICN_ID_PENDING, false); |
235 | #endif |
236 | |
237 | if (!TryMacNTP(*ntp1, *ntp2, *utc)) { |
238 | ShowInitIcon(MACNTP_ICN_ID_SUCCESS, true); |
239 | } else { |
240 | ShowInitIcon(MACNTP_ICN_ID_FAILURE, true); |
241 | } |
242 | |
243 | // I have no idea if unlock is needed here. Maybe not. |
244 | HUnlock(ntp1); |
245 | //DisposHandle(ntp1); |
246 | HUnlock(ntp2); |
247 | // For whatever reason, System 6 crashes on boot if we |
248 | // try to dispose this specific handle. Okay. |
249 | //DisposHandle(ntp2); |
250 | HUnlock(utc); |
251 | //DisposHandle(utc); |
252 | HUnlock(g_mtcp_init_handle); |
253 | skip: |
254 | error: |
255 | RestoreA4(); |
256 | } |