diff options
author | Lev Walkin <vlm@lionet.info> | 2004-08-07 03:52:26 +0000 |
---|---|---|
committer | Lev Walkin <vlm@lionet.info> | 2004-08-07 03:52:26 +0000 |
commit | 990063697e5aeb179956c9339252aa71149b70ad (patch) | |
tree | cb6793886f70cb3d2a3159857b9cfb19d9b46bfe /skeletons | |
parent | 0aca385502e7aded43df802f6b3f430196f139ca (diff) |
improved asn_GT2time() and added asn_time2GT() function
Diffstat (limited to 'skeletons')
-rw-r--r-- | skeletons/GeneralizedTime.c | 182 | ||||
-rw-r--r-- | skeletons/GeneralizedTime.h | 23 | ||||
-rw-r--r-- | skeletons/tests/check-GeneralizedTime.c | 89 |
3 files changed, 243 insertions, 51 deletions
diff --git a/skeletons/GeneralizedTime.c b/skeletons/GeneralizedTime.c index 1dadb92d..5a3120d1 100644 --- a/skeletons/GeneralizedTime.c +++ b/skeletons/GeneralizedTime.c @@ -21,7 +21,7 @@ asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = { "GeneralizedTime", GeneralizedTime_constraint, /* Check validity of time */ OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ - OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + GeneralizedTime_encode_der, /* Implemented in terms of OCTET STRING */ GeneralizedTime_print, OCTET_STRING_free, 0, /* Use generic outmost tag fetcher */ @@ -45,7 +45,7 @@ GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, time_t tloc; errno = EPERM; /* Just an unlikely error code */ - tloc = asn_GT2time(st, 0); + tloc = asn_GT2time(st, 0, 0); if(tloc == -1 && errno != EPERM) { _ASN_ERRLOG("%s: Invalid time format: %s", td->name, strerror(errno)); @@ -55,6 +55,47 @@ GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, return 0; } +der_enc_rval_t +GeneralizedTime_encode_der(asn1_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + GeneralizedTime_t *st = ptr; + der_enc_rval_t erval; + + /* If not canonical DER, re-encode into canonical DER. */ + if(st->size && st->buf[st->size-1] != 'Z') { + struct tm tm; + time_t tloc; + + errno = EPERM; + tloc = asn_GT2time(st, &tm, 1); /* Recognize time */ + if(tloc == -1 && errno != EPERM) { + /* Failed to recognize time. Fail completely. */ + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + st = asn_time2GT(0, &tm, 1); /* Save time canonically */ + if(!st) { + /* Memory allocation failure. */ + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + } + + erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key); + + if(st != ptr) { + FREEMEM(st->buf); + FREEMEM(st); + } + + return erval; +} + int GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { @@ -69,11 +110,11 @@ GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, int ret; errno = EPERM; - if(asn_GT2time(st, &tm) == -1 && errno != EPERM) + if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM) return cb("<bad-value>", 11, app_key); ret = snprintf(buf, sizeof(buf), - "%04d-%02d-%02d %02d:%02d%02d", + "%04d-%02d-%02d %02d:%02d%02d (GMT)", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); assert(ret > 0 && ret < (int)sizeof(buf)); @@ -96,19 +137,19 @@ GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, * Where to look for offset from GMT, Phase II. */ #ifdef HAVE_TM_ZONE -#define GMTOFF (tm_s.tm_gmtoff) +#define GMTOFF(tm) ((tm).tm_gmtoff) #else /* HAVE_TM_ZONE */ -#define GMTOFF (-timezone) +#define GMTOFF(tm) (-timezone) #endif /* HAVE_TM_ZONE */ time_t -asn_GT2time(const GeneralizedTime_t *st, struct tm *_tm) { +asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) { struct tm tm_s; uint8_t *buf; uint8_t *end; - int tm_gmtoff_h = 0; - int tm_gmtoff_m = 0; - int tm_gmtoff = 0; /* h + m */ + int gmtoff_h = 0; + int gmtoff_m = 0; + int gmtoff = 0; /* h + m */ int offset_specified = 0; time_t tloc; @@ -240,22 +281,22 @@ offset: return -1; } buf++; - B2F(tm_gmtoff_h); - B2F(tm_gmtoff_h); + B2F(gmtoff_h); + B2F(gmtoff_h); if(buf[-3] == 0x2D) /* Negative */ - tm_gmtoff = -1; + gmtoff = -1; else - tm_gmtoff = 1; + gmtoff = 1; if((end - buf) == 2) { - B2F(tm_gmtoff_m); - B2F(tm_gmtoff_m); + B2F(gmtoff_m); + B2F(gmtoff_m); } else if(end != buf) { errno = EINVAL; return -1; } - tm_gmtoff = tm_gmtoff * (3600 * tm_gmtoff_h + 60 * tm_gmtoff_m); + gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m); /* Fall through */ utc_finish: @@ -282,25 +323,110 @@ local_finish: tm_s.tm_year -= 1900; tm_s.tm_isdst = -1; - tloc = mktime(&tm_s); + tm_s.tm_sec -= gmtoff; + + /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/ + + if(offset_specified) + tloc = timegm(&tm_s); + else { + /* + * Without an offset (or 'Z'), + * we can only guess that it is a local zone. + * Interpret it in this fashion. + */ + tloc = mktime(&tm_s); + } if(tloc == -1) { errno = EINVAL; return -1; } - if(offset_specified) { - /* - * Offset from GMT is specified in the time expression. - */ - tloc += GMTOFF - tm_gmtoff; - if(_tm && (localtime_r(&tloc, &tm_s) == NULL)) { - /* Could not reconstruct the time */ - return -1; + if(ret_tm) { + if(as_gmt) { + if(offset_specified) { + *ret_tm = tm_s; + } else { + if(gmtime_r(&tloc, ret_tm) == 0) { + errno = EINVAL; + return -1; + } + } + } else { + if(localtime_r(&tloc, ret_tm) == 0) { + errno = EINVAL; + return -1; + } } } - if(_tm) memcpy(_tm, &tm_s, sizeof(struct tm)); - return tloc; } + +GeneralizedTime_t * +asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) { + struct tm tm_s; + long gmtoff; + const unsigned int buf_size = 24; /* 4+2+2 +2+2+2 +4 + cushion */ + char *buf; + char *p; + int size; + + /* Check arguments */ + if(!tm) { + errno = EINVAL; + return 0; + } + + /* Pre-allocate a buffer of sufficient yet small length */ + buf = MALLOC(buf_size); + if(!buf) return 0; + + gmtoff = GMTOFF(*tm); + + if(force_gmt && gmtoff) { + tm_s = *tm; + tm_s.tm_sec -= gmtoff; + timegm(&tm_s); /* Fix the time */ + assert(!GMTOFF(tm_s)); + tm = &tm_s; + } + + size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec + ); + assert(size == 14); + + p = buf + size; + if(force_gmt) { + *p++ = 0x5a; /* 'Z' */ + *p++ = 0; + size++; + } else { + int ret = snprintf(p, buf_size - size, "%+03ld%02ld", + gmtoff / 3600, gmtoff % 3600); + assert(ret >= 5 && ret <= 7); + size += ret; + } + + if(opt_gt) { + if(opt_gt->buf) + FREEMEM(opt_gt->buf); + } else { + opt_gt = CALLOC(1, sizeof *opt_gt); + if(!opt_gt) { free(buf); return 0; } + } + + opt_gt->buf = buf; + opt_gt->size = size; + + return opt_gt; +} + + diff --git a/skeletons/GeneralizedTime.h b/skeletons/GeneralizedTime.h index d8176be0..a7a8a4a2 100644 --- a/skeletons/GeneralizedTime.h +++ b/skeletons/GeneralizedTime.h @@ -13,14 +13,33 @@ typedef OCTET_STRING_t GeneralizedTime_t; /* Implemented using OCTET STRING */ extern asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime; asn_constr_check_f GeneralizedTime_constraint; +der_type_encoder_f GeneralizedTime_encode_der; asn_struct_print_f GeneralizedTime_print; /*********************** * Some handy helpers. * ***********************/ -/* On error returns -1 and errno set to EINVAL */ struct tm; /* <time.h> */ -time_t asn_GT2time(const GeneralizedTime_t *, struct tm *_optional_tm4fill); + +/* + * Convert a GeneralizedTime structure into time_t + * and optionally into struct tm. + * If as_gmt is given, the resulting _optional_tm4fill will have a GMT zone, + * instead of default local one. + * On error returns -1 and errno set to EINVAL + */ +time_t asn_GT2time(const GeneralizedTime_t *, struct tm *_optional_tm4fill, + int as_gmt); + +/* + * Convert a struct tm into GeneralizedTime. + * If __opt_gt is not given, this function will try to allocate one. + * If force_gmt is given, the resulting GeneralizedTime will be forced + * into a GMT time zone (encoding ends with 'Z'). + * On error, this function returns 0 and sets errno. + */ +GeneralizedTime_t *asn_time2GT(GeneralizedTime_t *__opt_gt, const struct tm *, + int force_gmt); #endif /* _GeneralizedTime_H_ */ diff --git a/skeletons/tests/check-GeneralizedTime.c b/skeletons/tests/check-GeneralizedTime.c index 3451cffe..5a6cb0b2 100644 --- a/skeletons/tests/check-GeneralizedTime.c +++ b/skeletons/tests/check-GeneralizedTime.c @@ -3,7 +3,7 @@ #include "../constraints.c" static void -check(char *time_str, time_t sample) { +check(char *time_str, time_t expect, int as_gmt) { GeneralizedTime_t gt; struct tm tm; time_t tloc; @@ -11,44 +11,91 @@ check(char *time_str, time_t sample) { gt.buf = time_str; gt.size = strlen(time_str); - tloc = asn_GT2time(>, &tm); - printf("[%s] -> %ld == %ld\n", time_str, (long)tloc, (long)sample); + tloc = asn_GT2time(>, &tm, as_gmt); + printf("%s: [%s] -> %ld == %ld\n", + as_gmt?"GMT":"ofs", time_str, (long)tloc, (long)expect); if(tloc != -1) - printf("\t%d-%d-%dT%02d:%02d:%02d %ld\n", + printf("\t%04d-%02d-%02dT%02d:%02d:%02d%+03ld%02ld\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - tm.tm_gmtoff + (tm.tm_gmtoff / 3600), + labs(tm.tm_gmtoff % 3600) ); - assert(tloc == sample); + assert(tloc == expect); + + assert(tloc == -1 || as_gmt == 0 || tm.tm_gmtoff == 0); + + if(!as_gmt) check(time_str, expect, 1); +} + +static void +rcheck(time_t tloc, const char *expect, int force_gmt) { + GeneralizedTime_t *gt; + struct tm tm, *tmp; + + tmp = localtime_r(&tloc, &tm); + assert(tmp); + + gt = asn_time2GT(0, &tm, force_gmt); + if(gt) { + assert(expect); + printf("[%s] vs [%s] (%d)\n", + gt->buf, expect, force_gmt); + assert(gt->size == strlen(gt->buf)); + assert(!strcmp(gt->buf, expect)); + } else { + assert(!expect); + } } int main(int ac, char **av) { - check("200401250", -1); - check("2004012509300", -1); - check("20040125093000-", -1); - check("20040125093007-0", -1); - check("20040125093007-080", -1); - check("200401250930.01Z", -1); + check("200401250", -1, 0); + check("2004012509300", -1, 0); + check("20040125093000-", -1, 0); + check("20040125093007-0", -1, 0); + check("20040125093007-080", -1, 0); + check("200401250930.01Z", -1, 0); + + /* These six are from X.690:11.7.5 */ + check("19920520240000Z", -1, 0); /* midnight represented incorrectly */ + //check("19920622123421.0Z", -1, 0); /* spurious trailing zeros */ + //check("19920722132100.30Z", -1, 0); /* spurious trailing zeros */ + check("19920521000000Z", 706406400, 0); + check("19920622123421Z", 709216461, 0); + check("19920722132100.3Z", 711811260, 0); - check("20040125093007Z", 1075023007); - check("20040125093007+00", 1075023007); - check("20040125093007.01+0000", 1075023007); - check("20040125093007,1+0000", 1075023007); - check("20040125093007-0800", 1075051807); + check("20040125093007Z", 1075023007, 0); + check("20040125093007+00", 1075023007, 0); + check("20040125093007.01+0000", 1075023007, 0); + check("20040125093007,1+0000", 1075023007, 0); + check("20040125093007-0800", 1075051807, 0); if(ac > 1) { /* These will be valid only inside PST time zone */ - check("20040125093007", 1075051807); - check("200401250930", 1075051800); - check("20040125093000,01", 1075051800); - check("20040125093000,1234", 1075051800); + check("20040125093007", 1075051807, 0); + check("200401250930", 1075051800, 0); + check("20040125093000,01", 1075051800, 0); + check("20040125093000,1234", 1075051800, 0); } + rcheck(1075023007, "20040125013007-0800", 0); + rcheck(1075023007, "20040125093007Z", 1); + return 0; } + +/* + * Dummy function. + */ + +der_enc_rval_t +OCTET_STRING_encode_der(asn1_TYPE_descriptor_t *td, void *ptr, int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + return erval; +} |