1 /* $NetBSD: safe_ultostr.c,v 1.1.1.2 2014/07/06 19:27:51 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* safe_ultostr 3 6 /* SUMMARY 7 /* convert unsigned long to safe string 8 /* SYNOPSIS 9 /* #include <safe_ultostr.h> 10 /* 11 /* char *safe_ultostr(result, ulval, base, padlen, padchar) 12 /* VSTRING *result; 13 /* unsigned long ulval; 14 /* int base; 15 /* int padlen; 16 /* int padchar; 17 /* 18 /* unsigned long safe_strtoul(start, end, base) 19 /* const char *start; 20 /* char **end; 21 /* int base; 22 /* DESCRIPTION 23 /* The functions in this module perform conversions between 24 /* unsigned long values and "safe" alphanumerical strings 25 /* (strings with digits, uppercase letters and lowercase 26 /* letters, but without the vowels AEIOUaeiou). Specifically, 27 /* the characters B-Z represent the numbers 10-30, and b-z 28 /* represent 31-51. 29 /* 30 /* safe_ultostr() converts an unsigned long value to a safe 31 /* alphanumerical string. This is the reverse of safe_strtoul(). 32 /* 33 /* safe_strtoul() implements similar functionality as strtoul() 34 /* except that it uses a safe alphanumerical string as input, 35 /* and that it supports no signs or 0/0x prefixes. 36 /* 37 /* Arguments: 38 /* .IP result 39 /* Buffer for storage of the result of conversion to string. 40 /* .IP ulval 41 /* Unsigned long value. 42 /* .IP base 43 /* Value between 2 and 52 inclusive. 44 /* .IP padlen 45 /* .IP padchar 46 /* Left-pad a short result with padchar characters to the 47 /* specified length. Specify padlen=0 to disable padding. 48 /* .IP start 49 /* Pointer to the first character of the string to be converted. 50 /* .IP end 51 /* On return, pointer to the first character not in the input 52 /* alphabet, or to the string terminator. 53 /* DIAGNOSTICS 54 /* Fatal: out of memory. 55 /* 56 /* safe_strtoul() returns (0, EINVAL) when no conversion could 57 /* be performed, and (ULONG_MAX, ERANGE) in case of overflow. 58 /* LICENSE 59 /* .ad 60 /* .fi 61 /* The Secure Mailer license must be distributed with this software. 62 /* AUTHOR(S) 63 /* Wietse Venema 64 /* IBM T.J. Watson Research 65 /* P.O. Box 704 66 /* Yorktown Heights, NY 10598, USA 67 /*--*/ 68 69 /* System library. */ 70 71 #include <sys_defs.h> 72 #include <stdlib.h> 73 #include <limits.h> 74 #include <errno.h> 75 #include <ctype.h> 76 77 /* Utility library. */ 78 79 #include <msg.h> 80 #include <vstring.h> 81 #include <mymalloc.h> 82 83 /* Global library. */ 84 85 #include <safe_ultostr.h> 86 87 /* Application-specific. */ 88 89 #define STR vstring_str 90 #define END vstring_end 91 #define SWAP(type, a, b) { type temp; temp = a; a = b; b = temp; } 92 93 static unsigned char safe_chars[] = 94 "0123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; 95 96 #define SAFE_MAX_BASE (sizeof(safe_chars) - 1) 97 #define SAFE_MIN_BASE (2) 98 99 /* safe_ultostr - convert unsigned long to safe alphanumerical string */ 100 101 char *safe_ultostr(VSTRING *buf, unsigned long ulval, int base, 102 int padlen, int padchar) 103 { 104 const char *myname = "safe_ultostr"; 105 char *start; 106 char *last; 107 int i; 108 109 /* 110 * Sanity check. 111 */ 112 if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) 113 msg_panic("%s: bad base: %d", myname, base); 114 115 /* 116 * First accumulate the result, backwards. 117 */ 118 VSTRING_RESET(buf); 119 while (ulval != 0) { 120 VSTRING_ADDCH(buf, safe_chars[ulval % base]); 121 ulval /= base; 122 } 123 while (VSTRING_LEN(buf) < padlen) 124 VSTRING_ADDCH(buf, padchar); 125 VSTRING_TERMINATE(buf); 126 127 /* 128 * Then, reverse the result. 129 */ 130 start = STR(buf); 131 last = END(buf) - 1; 132 for (i = 0; i < VSTRING_LEN(buf) / 2; i++) 133 SWAP(int, start[i], last[-i]); 134 return (STR(buf)); 135 } 136 137 /* safe_strtoul - convert safe alphanumerical string to unsigned long */ 138 139 unsigned long safe_strtoul(const char *start, char **end, int base) 140 { 141 const char *myname = "safe_strtoul"; 142 static unsigned char *char_map = 0; 143 unsigned char *cp; 144 unsigned long sum; 145 unsigned long div_limit; 146 unsigned long mod_limit; 147 int char_val; 148 149 /* 150 * Sanity check. 151 */ 152 if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) 153 msg_panic("%s: bad base: %d", myname, base); 154 155 /* 156 * One-time initialization. Assume 8-bit bytes. 157 */ 158 if (char_map == 0) { 159 char_map = (unsigned char *) mymalloc(256); 160 for (char_val = 0; char_val < 256; char_val++) 161 char_map[char_val] = SAFE_MAX_BASE; 162 for (char_val = 0; char_val < SAFE_MAX_BASE; char_val++) 163 char_map[safe_chars[char_val]] = char_val; 164 } 165 166 /* 167 * Per-call initialization. 168 */ 169 sum = 0; 170 div_limit = ULONG_MAX / base; 171 mod_limit = ULONG_MAX % base; 172 173 /* 174 * Skip leading whitespace. We don't implement sign/base prefixes. 175 */ 176 if (end) 177 *end = (char *) start; 178 while (ISSPACE(*start)) 179 ++start; 180 181 /* 182 * Start the conversion. 183 */ 184 errno = 0; 185 for (cp = (unsigned char *) start; (char_val = char_map[*cp]) < base; cp++) { 186 /* Return (ULONG_MAX, ERANGE) if the result is too large. */ 187 if (sum > div_limit 188 || (sum == div_limit && char_val > mod_limit)) { 189 sum = ULONG_MAX; 190 errno = ERANGE; 191 /* Skip "valid" characters, per the strtoul() spec. */ 192 while (char_map[*++cp] < base) 193 /* void */ ; 194 break; 195 } 196 sum = sum * base + char_val; 197 } 198 /* Return (0, EINVAL) after no conversion. Test moved here 20131209. */ 199 if (cp == (unsigned char *) start) 200 errno = EINVAL; 201 else if (end) 202 *end = (char *) cp; 203 return (sum); 204 } 205 206 #ifdef TEST 207 208 /* 209 * Proof-of-concept test program. Read a number from stdin, convert to 210 * string, and print the result. 211 */ 212 #include <stdio.h> /* sscanf */ 213 #include <vstream.h> 214 #include <vstring_vstream.h> 215 216 int main(int unused_argc, char **unused_argv) 217 { 218 VSTRING *buf = vstring_alloc(100); 219 char *junk; 220 unsigned long ulval; 221 int base; 222 char ch; 223 unsigned long ulval2; 224 225 #ifdef MISSING_STRTOUL 226 #define strtoul strtol 227 #endif 228 229 /* 230 * Hard-coded string-to-number test. 231 */ 232 ulval2 = safe_strtoul(" ", &junk, 10); 233 if (*junk == 0 || errno != EINVAL) 234 msg_warn("input=' ' result=%lu errno=%m", ulval2); 235 236 /* 237 * Configurable number-to-string-to-number test. 238 */ 239 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { 240 ch = 0; 241 if (sscanf(STR(buf), "%lu %d%c", &ulval, &base, &ch) != 2 || ch) { 242 msg_warn("bad input %s", STR(buf)); 243 } else { 244 (void) safe_ultostr(buf, ulval, base, 5, '0'); 245 vstream_printf("%lu = %s\n", ulval, STR(buf)); 246 ulval2 = safe_strtoul(STR(buf), &junk, base); 247 if (*junk || (ulval2 == ULONG_MAX && errno == ERANGE)) 248 msg_warn("%s: %m", STR(buf)); 249 if (ulval2 != ulval) 250 msg_warn("%lu != %lu", ulval2, ulval); 251 } 252 vstream_fflush(VSTREAM_OUT); 253 } 254 vstring_free(buf); 255 return (0); 256 } 257 258 #endif 259