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