Replace snprintf() by SCTP_SNPRINTF() and let SCTP_SNPRINTF() map
to snprintf() on FreeBSD. This allows to check for failures of snprintf() on platforms other than FreeBSD kernel.
This commit is contained in:
parent
821bae7cf3
commit
999f86d67d
@ -1706,8 +1706,7 @@ sctp_handle_asconf_ack(struct mbuf *m, int offset,
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf_ack: got unexpected next serial number! Aborting asoc!\n");
|
||||
snprintf(msg, sizeof(msg), "Never sent serial number %8.8x",
|
||||
serial_num);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Never sent serial number %8.8x", serial_num);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED);
|
||||
*abort_no_unlock = 1;
|
||||
|
@ -434,7 +434,7 @@ sctp_abort_in_reasm(struct sctp_tcb *stcb,
|
||||
struct mbuf *oper;
|
||||
|
||||
if (stcb->asoc.idata_supported) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Reass %x,CF:%x,TSN=%8.8x,SID=%4.4x,FSN=%8.8x,MID:%8.8x",
|
||||
opspot,
|
||||
control->fsn_included,
|
||||
@ -442,7 +442,7 @@ sctp_abort_in_reasm(struct sctp_tcb *stcb,
|
||||
chk->rec.data.sid,
|
||||
chk->rec.data.fsn, chk->rec.data.mid);
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Reass %x,CI:%x,TSN=%8.8x,SID=%4.4x,FSN=%4.4x,SSN:%4.4x",
|
||||
opspot,
|
||||
control->fsn_included,
|
||||
@ -533,11 +533,11 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb,
|
||||
*/
|
||||
TAILQ_INSERT_HEAD(&strm->inqueue, control, next_instrm);
|
||||
if (asoc->idata_supported) {
|
||||
snprintf(msg, sizeof(msg), "Delivered MID=%8.8x, got TSN=%8.8x, SID=%4.4x, MID=%8.8x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Delivered MID=%8.8x, got TSN=%8.8x, SID=%4.4x, MID=%8.8x",
|
||||
strm->last_mid_delivered, control->sinfo_tsn,
|
||||
control->sinfo_stream, control->mid);
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "Delivered SSN=%4.4x, got TSN=%8.8x, SID=%4.4x, SSN=%4.4x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Delivered SSN=%4.4x, got TSN=%8.8x, SID=%4.4x, SSN=%4.4x",
|
||||
(uint16_t)strm->last_mid_delivered,
|
||||
control->sinfo_tsn,
|
||||
control->sinfo_stream,
|
||||
@ -648,9 +648,8 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb,
|
||||
* to put it on the queue.
|
||||
*/
|
||||
if (sctp_place_control_in_stream(strm, asoc, control)) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"Queue to str MID: %u duplicate",
|
||||
control->mid);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Queue to str MID: %u duplicate", control->mid);
|
||||
sctp_clean_up_control(stcb, control);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_3;
|
||||
@ -1881,8 +1880,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
* can *not* be fsn 0. XXX: This can happen in case of a
|
||||
* wrap around. Ignore is for now.
|
||||
*/
|
||||
snprintf(msg, sizeof(msg), "FSN zero for MID=%8.8x, but flags=%2.2x",
|
||||
mid, chk_flags);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "FSN zero for MID=%8.8x, but flags=%2.2x", mid, chk_flags);
|
||||
goto err_out;
|
||||
}
|
||||
control = sctp_find_reasm_entry(&asoc->strmin[sid], mid, ordered, asoc->idata_supported);
|
||||
@ -1893,7 +1891,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
if (control != NULL) {
|
||||
/* We found something, does it belong? */
|
||||
if (ordered && (mid != control->mid)) {
|
||||
snprintf(msg, sizeof(msg), "Reassembly problem (MID=%8.8x)", mid);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Reassembly problem (MID=%8.8x)", mid);
|
||||
err_out:
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_16;
|
||||
@ -1906,7 +1904,8 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
* We can't have a switched order with an
|
||||
* unordered chunk
|
||||
*/
|
||||
snprintf(msg, sizeof(msg), "All fragments of a user message must be ordered or unordered (TSN=%8.8x)",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"All fragments of a user message must be ordered or unordered (TSN=%8.8x)",
|
||||
tsn);
|
||||
goto err_out;
|
||||
}
|
||||
@ -1915,7 +1914,8 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
* We can't have a switched unordered with a
|
||||
* ordered chunk
|
||||
*/
|
||||
snprintf(msg, sizeof(msg), "All fragments of a user message must be ordered or unordered (TSN=%8.8x)",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"All fragments of a user message must be ordered or unordered (TSN=%8.8x)",
|
||||
tsn);
|
||||
goto err_out;
|
||||
}
|
||||
@ -1930,12 +1930,14 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
if (ordered || asoc->idata_supported) {
|
||||
SCTPDBG(SCTP_DEBUG_XXX, "chunk_flags: 0x%x dup detected on MID: %u\n",
|
||||
chk_flags, mid);
|
||||
snprintf(msg, sizeof(msg), "Duplicate MID=%8.8x detected.", mid);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Duplicate MID=%8.8x detected.", mid);
|
||||
goto err_out;
|
||||
} else {
|
||||
if ((tsn == control->fsn_included + 1) &&
|
||||
(control->end_added == 0)) {
|
||||
snprintf(msg, sizeof(msg), "Illegal message sequence, missing end for MID: %8.8x", control->fsn_included);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Illegal message sequence, missing end for MID: %8.8x",
|
||||
control->fsn_included);
|
||||
goto err_out;
|
||||
} else {
|
||||
control = NULL;
|
||||
@ -2032,13 +2034,13 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
mid, asoc->strmin[sid].last_mid_delivered);
|
||||
|
||||
if (asoc->idata_supported) {
|
||||
snprintf(msg, sizeof(msg), "Delivered MID=%8.8x, got TSN=%8.8x, SID=%4.4x, MID=%8.8x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Delivered MID=%8.8x, got TSN=%8.8x, SID=%4.4x, MID=%8.8x",
|
||||
asoc->strmin[sid].last_mid_delivered,
|
||||
tsn,
|
||||
sid,
|
||||
mid);
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "Delivered SSN=%4.4x, got TSN=%8.8x, SID=%4.4x, SSN=%4.4x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Delivered SSN=%4.4x, got TSN=%8.8x, SID=%4.4x, SSN=%4.4x",
|
||||
(uint16_t)asoc->strmin[sid].last_mid_delivered,
|
||||
tsn,
|
||||
sid,
|
||||
@ -2769,7 +2771,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "%s", "I-DATA chunk received when DATA was negotiated");
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "%s", "I-DATA chunk received when DATA was negotiated");
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_20;
|
||||
sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED);
|
||||
@ -2780,7 +2782,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "%s", "DATA chunk received when I-DATA was negotiated");
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "%s", "DATA chunk received when I-DATA was negotiated");
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_21;
|
||||
sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED);
|
||||
@ -2803,7 +2805,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "%s chunk of length %u",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "%s chunk of length %u",
|
||||
ch->chunk_type == SCTP_DATA ? "DATA" : "I-DATA",
|
||||
chk_length);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
@ -2874,7 +2876,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "DATA chunk followed by chunk of type %2.2x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "DATA chunk followed by chunk of type %2.2x",
|
||||
ch->chunk_type);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED);
|
||||
@ -2893,8 +2895,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "Chunk of length %u",
|
||||
chk_length);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "Chunk of length %u", chk_length);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_23;
|
||||
sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED);
|
||||
@ -4043,7 +4044,8 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack,
|
||||
|
||||
*abort_now = 1;
|
||||
/* XXX */
|
||||
snprintf(msg, sizeof(msg), "Cum ack %8.8x greater or equal than TSN %8.8x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Cum ack %8.8x greater or equal than TSN %8.8x",
|
||||
cumack, send_s);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24;
|
||||
@ -4585,7 +4587,8 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup,
|
||||
hopeless_peer:
|
||||
*abort_now = 1;
|
||||
/* XXX */
|
||||
snprintf(msg, sizeof(msg), "Cum ack %8.8x greater or equal than TSN %8.8x",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"Cum ack %8.8x greater or equal than TSN %8.8x",
|
||||
cum_ack, send_s);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_28;
|
||||
@ -5629,7 +5632,7 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb,
|
||||
* give out). This must be an attacker.
|
||||
*/
|
||||
*abort_flag = 1;
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"New cum ack %8.8x too high, highest TSN %8.8x",
|
||||
new_cum_tsn, asoc->highest_tsn_inside_map);
|
||||
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
|
||||
|
@ -4692,7 +4692,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
|
||||
}
|
||||
}
|
||||
if (stcb == NULL) {
|
||||
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
/* no association, so it's out of the blue... */
|
||||
@ -4734,7 +4734,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
|
||||
if (stcb != NULL) {
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
}
|
||||
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
sctp_handle_ootb(m, iphlen, *offset, src, dst,
|
||||
@ -5671,7 +5671,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
|
||||
SCTP_TCB_UNLOCK(stcb);
|
||||
stcb = NULL;
|
||||
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
|
||||
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err,
|
||||
@ -5733,7 +5733,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
|
||||
if (stcb == NULL) {
|
||||
/* out of the blue DATA chunk */
|
||||
SCTP_PROBE5(receive, NULL, NULL, m, NULL, sh);
|
||||
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err,
|
||||
@ -5799,7 +5799,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
|
||||
/*
|
||||
* We consider OOTB any data sent during asoc setup.
|
||||
*/
|
||||
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err,
|
||||
|
@ -295,6 +295,8 @@ typedef struct callout sctp_os_timer_t;
|
||||
|
||||
#define SCTP_ALIGN_TO_END(m, len) M_ALIGN(m, len)
|
||||
|
||||
#define SCTP_SNPRINTF(...) snprintf(__VA_ARGS__)
|
||||
|
||||
/* We make it so if you have up to 4 threads
|
||||
* writing based on the default size of
|
||||
* the packet log 65 k, that would be
|
||||
|
@ -5581,7 +5581,7 @@ do_a_abort:
|
||||
if (op_err == NULL) {
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
}
|
||||
@ -6758,7 +6758,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
abort_anyway:
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
@ -9626,7 +9626,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
|
||||
struct mbuf *op_err;
|
||||
char msg[SCTP_DIAG_INFO_LEN];
|
||||
|
||||
snprintf(msg, sizeof(msg), "TSN %8.8x retransmitted %d times, giving up",
|
||||
SCTP_SNPRINTF(msg, sizeof(msg), "TSN %8.8x retransmitted %d times, giving up",
|
||||
chk->rec.data.tsn, chk->snd_count);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
@ -13610,7 +13610,7 @@ dataless_eof:
|
||||
atomic_add_int(&stcb->asoc.refcnt, -1);
|
||||
free_cnt_applied = 0;
|
||||
}
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
|
@ -544,9 +544,9 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index,
|
||||
atomic_add_int(&vrf->refcount, 1);
|
||||
sctp_ifnp->ifn_mtu = SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index, addr->sa_family);
|
||||
if (if_name != NULL) {
|
||||
snprintf(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s", if_name);
|
||||
SCTP_SNPRINTF(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s", if_name);
|
||||
} else {
|
||||
snprintf(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s", "unknown");
|
||||
SCTP_SNPRINTF(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s", "unknown");
|
||||
}
|
||||
hash_ifn_head = &SCTP_BASE_INFO(vrf_ifn_hash)[(ifn_index & SCTP_BASE_INFO(vrf_ifn_hashmark))];
|
||||
LIST_INIT(&sctp_ifnp->ifalist);
|
||||
@ -6217,7 +6217,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* in setup state we
|
||||
* abort this guy
|
||||
*/
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
@ -6317,7 +6317,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* in setup state we
|
||||
* abort this guy
|
||||
*/
|
||||
snprintf(msg, sizeof(msg),
|
||||
SCTP_SNPRINTF(msg, sizeof(msg),
|
||||
"%s:%d at %s", __FILE__, __LINE__, __func__);
|
||||
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
|
||||
msg);
|
||||
|
Loading…
x
Reference in New Issue
Block a user