]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/network_agent.c
xnu-4570.71.2.tar.gz
[apple/xnu.git] / bsd / net / network_agent.c
index a51eacfb88cd331a0ac1d1da98d0d2a359bacd86..392665f18c2d5b04a0eab98dd3cfbd9ba1acd18d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, 2017 Apple Inc. All rights reserved.
+ * Copyright (c) 2014-2018 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -80,6 +80,8 @@ LIST_HEAD(netagent_client_list_s, netagent_client);
 struct netagent_wrapper {
        LIST_ENTRY(netagent_wrapper) master_chain;
        u_int32_t control_unit;
+       netagent_event_f event_handler;
+       void *event_context;
        u_int32_t generation;
        u_int64_t use_count;
        struct netagent_client_list_s pending_triggers_list;
@@ -87,8 +89,10 @@ struct netagent_wrapper {
 };
 
 struct netagent_session {
-       u_int32_t control_unit;
+       u_int32_t control_unit; // A control unit of 0 indicates an agent owned by the kernel
        struct netagent_wrapper *wrapper;
+       netagent_event_f event_handler;
+       void *event_context;
 };
 
 typedef enum {
@@ -608,6 +612,18 @@ done:
        return (new_session);
 }
 
+netagent_session_t netagent_create(netagent_event_f event_handler, void *context)
+{
+       struct netagent_session *session = netagent_create_session(0);
+       if (session == NULL) {
+               return NULL;
+       }
+
+       session->event_handler = event_handler;
+       session->event_context = context;
+       return session;
+}
+
 static void
 netagent_free_wrapper(struct netagent_wrapper *wrapper)
 {
@@ -668,6 +684,11 @@ netagent_delete_session(struct netagent_session *session)
        }
 }
 
+void netagent_destroy(netagent_session_t session)
+{
+       return netagent_delete_session((struct netagent_session *)session);
+}
+
 static int
 netagent_packet_get_netagent_data_size(mbuf_t packet, int offset, int *err)
 {
@@ -693,6 +714,8 @@ netagent_handle_register_inner(struct netagent_session *session, struct netagent
        lck_rw_lock_exclusive(&netagent_lock);
 
        new_wrapper->control_unit = session->control_unit;
+       new_wrapper->event_handler = session->event_handler;
+       new_wrapper->event_context = session->event_context;
        new_wrapper->generation = g_next_generation++;
 
        session->wrapper = new_wrapper;
@@ -710,6 +733,56 @@ netagent_handle_register_inner(struct netagent_session *session, struct netagent
        return 0;
 }
 
+errno_t
+netagent_register(netagent_session_t _session, struct netagent *agent)
+{
+       int data_size = 0;
+       struct netagent_wrapper *new_wrapper = NULL;
+
+       struct netagent_session *session = (struct netagent_session *)_session;
+       if (session == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot register agent on NULL session");
+               return EINVAL;
+       }
+
+       if (agent == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot register NULL agent");
+               return EINVAL;
+       }
+
+       if (session->wrapper != NULL) {
+               NETAGENTLOG0(LOG_ERR, "Session already has a registered agent");
+               return EINVAL;
+       }
+
+       data_size = agent->netagent_data_size;
+       if (data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) {
+               NETAGENTLOG(LOG_ERR, "Register message size could not be read, data_size %d",
+                                       data_size);
+               return EINVAL;
+       }
+
+       MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
+       if (new_wrapper == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
+               return ENOMEM;
+       }
+
+       memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
+       memcpy(&new_wrapper->netagent, agent, sizeof(struct netagent) + data_size);
+
+       int error = netagent_handle_register_inner(session, new_wrapper);
+       if (error != 0) {
+               FREE(new_wrapper, M_NETAGENT);
+               return error;
+       }
+
+       NETAGENTLOG0(LOG_DEBUG, "Registered new agent");
+       netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED, TRUE);
+
+       return 0;
+}
+
 static errno_t
 netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payload,
                                                                u_int32_t payload_length)
@@ -738,7 +811,7 @@ netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payl
        }
 
        if (payload_length < sizeof(struct netagent)) {
-               NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
+               NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%u < %lu)",
                                        payload_length, sizeof(struct netagent));
                response_error = EINVAL;
                goto done;
@@ -752,7 +825,7 @@ netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payl
        }
 
        if (payload_length != (sizeof(struct netagent) + data_size)) {
-               NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%u != %u)", (sizeof(struct netagent) + data_size), payload_length);
+               NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%lu != %u)", (sizeof(struct netagent) + data_size), payload_length);
                response_error = EINVAL;
                goto done;
     }
@@ -804,7 +877,7 @@ netagent_handle_register_message(struct netagent_session *session, u_int32_t mes
        }
 
        if (payload_length < sizeof(struct netagent)) {
-               NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
+               NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%u < %lu)",
                                        payload_length, sizeof(struct netagent));
                response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA;
                goto fail;
@@ -846,6 +919,19 @@ fail:
        netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_REGISTER, message_id, response_error);
 }
 
+errno_t
+netagent_unregister(netagent_session_t _session)
+{
+       struct netagent_session *session = (struct netagent_session *)_session;
+       if (session == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot unregister NULL session");
+               return EINVAL;
+       }
+
+       netagent_unregister_session_wrapper(session);
+       return 0;
+}
+
 static errno_t
 netagent_handle_unregister_setopt(struct netagent_session *session, u_int8_t *payload,
                                                                  u_int32_t payload_length)
@@ -978,6 +1064,8 @@ netagent_handle_update_inner(struct netagent_session *session, struct netagent_w
        netagent_free_wrapper(session->wrapper);
        session->wrapper = new_wrapper;
        new_wrapper->control_unit = session->control_unit;
+       new_wrapper->event_handler = session->event_handler;
+       new_wrapper->event_context = session->event_context;
        LIST_INSERT_HEAD(&master_netagent_list, new_wrapper, master_chain);
        LIST_INIT(&new_wrapper->pending_triggers_list);
 
@@ -989,6 +1077,59 @@ netagent_handle_update_inner(struct netagent_session *session, struct netagent_w
        return response_error;
 }
 
+errno_t
+netagent_update(netagent_session_t _session, struct netagent *agent)
+{
+       u_int8_t agent_changed;
+       int data_size = 0;
+       struct netagent_wrapper *new_wrapper = NULL;
+
+       struct netagent_session *session = (struct netagent_session *)_session;
+       if (session == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot update agent on NULL session");
+               return EINVAL;
+       }
+
+       if (agent == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot register NULL agent");
+               return EINVAL;
+       }
+
+       if (session->wrapper == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Session has no agent to update");
+               return EINVAL;
+       }
+
+       data_size = agent->netagent_data_size;
+       if (data_size > NETAGENT_MAX_DATA_SIZE) {
+               NETAGENTLOG(LOG_ERR, "Update message size (%u > %u) too large", data_size, NETAGENT_MAX_DATA_SIZE);
+               return EINVAL;
+       }
+
+       MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
+       if (new_wrapper == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
+               return ENOMEM;
+       }
+
+       memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
+       memcpy(&new_wrapper->netagent, agent, sizeof(struct netagent) + data_size);
+
+       int error = netagent_handle_update_inner(session, new_wrapper, data_size, &agent_changed, kNetagentErrorDomainPOSIX);
+       if (error == 0) {
+               netagent_post_event(session->wrapper->netagent.netagent_uuid, KEV_NETAGENT_UPDATED, agent_changed);
+               if (agent_changed == FALSE) {
+                       // The session wrapper does not need the "new_wrapper" as nothing changed
+                       FREE(new_wrapper, M_NETAGENT);
+               }
+       } else {
+               FREE(new_wrapper, M_NETAGENT);
+               return error;
+       }
+
+       return 0;
+}
+
 static errno_t
 netagent_handle_update_setopt(struct netagent_session *session, u_int8_t *payload, u_int32_t payload_length)
 {
@@ -1017,7 +1158,7 @@ netagent_handle_update_setopt(struct netagent_session *session, u_int8_t *payloa
        }
 
        if (payload_length < sizeof(struct netagent)) {
-               NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%d < %d)",
+               NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%u < %lu)",
                                        payload_length, sizeof(struct netagent));
                response_error = EINVAL;
                goto done;
@@ -1031,7 +1172,7 @@ netagent_handle_update_setopt(struct netagent_session *session, u_int8_t *payloa
        }
 
        if (payload_length != (sizeof(struct netagent) + data_size)) {
-               NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%u != %u)", (sizeof(struct netagent) + data_size), payload_length);
+               NETAGENTLOG(LOG_ERR, "Mismatch between data size and payload length (%lu != %u)", (sizeof(struct netagent) + data_size), payload_length);
                response_error = EINVAL;
                goto done;
     }
@@ -1084,7 +1225,7 @@ netagent_handle_update_message(struct netagent_session *session, u_int32_t messa
        }
 
        if (payload_length < sizeof(struct netagent)) {
-               NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%d < %d)",
+               NETAGENTLOG(LOG_ERR, "Update message size too small for agent: (%u < %lu)",
                                        payload_length, sizeof(struct netagent));
                response_error = NETAGENT_MESSAGE_ERROR_INVALID_DATA;
                goto fail;
@@ -1182,6 +1323,57 @@ fail:
        netagent_send_error_response(session, NETAGENT_MESSAGE_TYPE_GET, message_id, response_error);
 }
 
+errno_t
+netagent_assign_nexus(netagent_session_t _session, uuid_t necp_client_uuid,
+                                         void *assign_message, size_t assigned_results_length)
+{
+       struct netagent_session *session = (struct netagent_session *)_session;
+       if (session == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot assign nexus from NULL session");
+               return EINVAL;
+       }
+
+       if (session->wrapper == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Session has no agent");
+               return ENOENT;
+       }
+
+       // Note that if the error is 0, NECP has taken over our malloc'ed buffer
+       int error = necp_assign_client_result(session->wrapper->netagent.netagent_uuid, necp_client_uuid, assign_message, assigned_results_length);
+       if (error) {
+               // necp_assign_client_result returns POSIX errors; don't error for ENOENT
+               NETAGENTLOG((error == ENOENT ? LOG_DEBUG : LOG_ERR), "Client assignment failed: %d", error);
+               return error;
+       }
+
+       NETAGENTLOG0(LOG_DEBUG, "Agent assigned nexus properties to client");
+       return 0;
+}
+
+errno_t
+netagent_update_flow_protoctl_event(netagent_session_t _session,
+    uuid_t client_id, uint32_t protoctl_event_code,
+    uint32_t protoctl_event_val, uint32_t protoctl_event_tcp_seq_number)
+{
+       struct netagent_session *session = (struct netagent_session *)_session;
+       int error = 0;
+
+       if (session == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Cannot assign nexus from NULL session");
+               return (EINVAL);
+       }
+
+       if (session->wrapper == NULL) {
+               NETAGENTLOG0(LOG_ERR, "Session has no agent");
+               return (ENOENT);
+       }
+
+       error = necp_update_flow_protoctl_event(session->wrapper->netagent.netagent_uuid,
+           client_id, protoctl_event_code, protoctl_event_val, protoctl_event_tcp_seq_number);
+
+       return (error);
+}
+
 static errno_t
 netagent_handle_assign_nexus_setopt(struct netagent_session *session, u_int8_t *payload,
                                                                        u_int32_t payload_length)
@@ -1334,7 +1526,7 @@ netagent_handle_use_count_setopt(struct netagent_session *session, u_int8_t *pay
        }
 
        if (payload_length != sizeof(use_count)) {
-               NETAGENTLOG(LOG_ERR, "Payload length is invalid (%u)", payload_length);
+               NETAGENTLOG(LOG_ERR, "Payload length is invalid (%lu)", payload_length);
                response_error = EINVAL;
                goto done;
        }
@@ -1377,7 +1569,7 @@ netagent_handle_use_count_getopt(struct netagent_session *session, u_int8_t *buf
        }
 
        if (*buffer_length != sizeof(use_count)) {
-               NETAGENTLOG(LOG_ERR, "Buffer length is invalid (%u)", buffer_length);
+               NETAGENTLOG(LOG_ERR, "Buffer length is invalid (%lu)", *buffer_length);
                response_error = EINVAL;
                goto done;
        }
@@ -1676,7 +1868,13 @@ done:
 }
 
 int
-netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t message_type)
+netagent_client_message_with_params(uuid_t agent_uuid,
+                                                                       uuid_t necp_client_uuid,
+                                                                       pid_t pid,
+                                                                       u_int8_t message_type,
+                                                                       struct necp_client_nexus_parameters *parameters,
+                                                                       void **assigned_results,
+                                                                       size_t *assigned_results_length)
 {
        int error = 0;
 
@@ -1684,7 +1882,8 @@ netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t mes
                message_type != NETAGENT_MESSAGE_TYPE_CLIENT_ASSERT &&
                message_type != NETAGENT_MESSAGE_TYPE_CLIENT_UNASSERT &&
                message_type != NETAGENT_MESSAGE_TYPE_REQUEST_NEXUS &&
-               message_type != NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS) {
+               message_type != NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS &&
+               message_type != NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
                NETAGENTLOG(LOG_ERR, "Client netagent message type (%d) is invalid", message_type);
                return(EINVAL);
        }
@@ -1693,7 +1892,7 @@ netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t mes
        bool should_unlock = TRUE;
        struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(agent_uuid);
        if (wrapper == NULL) {
-               NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance could not be found");
+               NETAGENTLOG0(LOG_DEBUG, "Requested netagent for nexus instance could not be found");
                error = ENOENT;
                goto done;
        }
@@ -1704,19 +1903,25 @@ netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t mes
                        // Don't log, since this is a common case used to trigger events that cellular data is blocked, etc.
                        error = ENOTSUP;
 
-                       struct proc *p = current_proc();
-                       pid_t current_pid = 0;
-                       uuid_t current_proc_uuid;
-                       uuid_clear(current_proc_uuid);
-                       if (p != NULL) {
-                               current_pid = proc_pid(p);
-                               proc_getexecutableuuid(p, current_proc_uuid, sizeof(current_proc_uuid));
+
+                       pid_t report_pid = 0;
+                       uuid_t report_proc_uuid = {};
+                       if (parameters != NULL) {
+                               report_pid = parameters->epid;
+                               uuid_copy(report_proc_uuid, parameters->euuid);
+                       } else {
+                               struct proc *p = current_proc();
+                               if (p != NULL) {
+                                       report_pid = proc_pid(p);
+                                       proc_getexecutableuuid(p, report_proc_uuid, sizeof(report_proc_uuid));
+                               }
                        }
-                       netagent_send_cellular_failed_event(wrapper, current_pid, current_proc_uuid);
+                       netagent_send_cellular_failed_event(wrapper, report_pid, report_proc_uuid);
                        goto done;
                }
        } else if (message_type == NETAGENT_MESSAGE_TYPE_REQUEST_NEXUS ||
-                          message_type == NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS) {
+                          message_type == NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS ||
+                          message_type == NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
                if ((wrapper->netagent.netagent_flags & NETAGENT_FLAG_NEXUS_PROVIDER) == 0) {
                        NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance is not a nexus provider");
                        // Agent is not a nexus provider
@@ -1732,29 +1937,59 @@ netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, u_int8_t mes
                }
        }
 
-       error = netagent_send_client_message(wrapper, necp_client_uuid, message_type);
-       if (error == 0 && message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
-               if (lck_rw_lock_shared_to_exclusive(&netagent_lock)) {
-                       // Grab the lock exclusively to add a pending client to the list
-                       struct netagent_client *new_pending_client = NULL;
-                       MALLOC(new_pending_client, struct netagent_client *, sizeof(*new_pending_client), M_NETAGENT, M_WAITOK);
-                       if (new_pending_client == NULL) {
-                               NETAGENTLOG0(LOG_ERR, "Failed to allocate client for trigger");
-                       } else {
-                               uuid_copy(new_pending_client->client_id, necp_client_uuid);
-                               struct proc *p = current_proc();
-                               if (p != NULL) {
-                                       new_pending_client->client_pid = proc_pid(p);
-                                       proc_getexecutableuuid(p, new_pending_client->client_proc_uuid, sizeof(new_pending_client->client_proc_uuid));
+       if (wrapper->control_unit == 0) {
+               should_unlock = FALSE;
+               lck_rw_done(&netagent_lock);
+               if (wrapper->event_handler == NULL) {
+                       // No event handler registered for kernel agent
+                       error = EINVAL;
+               } else {
+                       error = wrapper->event_handler(message_type, necp_client_uuid, pid, wrapper->event_context, parameters, assigned_results, assigned_results_length);
+                       if (error != 0) {
+                               VERIFY(assigned_results == NULL || *assigned_results == NULL);
+                               VERIFY(assigned_results_length == NULL || *assigned_results_length == 0);
+                       }
+               }
+       } else {
+               // ABORT_NEXUS is kernel-private, so translate it for userspace nexus
+               if (message_type == NETAGENT_MESSAGE_TYPE_ABORT_NEXUS) {
+                       message_type = NETAGENT_MESSAGE_TYPE_CLOSE_NEXUS;
+               }
+
+               error = netagent_send_client_message(wrapper, necp_client_uuid, message_type);
+               if (error == 0 && message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
+                       if (lck_rw_lock_shared_to_exclusive(&netagent_lock)) {
+                               // Grab the lock exclusively to add a pending client to the list
+                               struct netagent_client *new_pending_client = NULL;
+                               MALLOC(new_pending_client, struct netagent_client *, sizeof(*new_pending_client), M_NETAGENT, M_WAITOK);
+                               if (new_pending_client == NULL) {
+                                       NETAGENTLOG0(LOG_ERR, "Failed to allocate client for trigger");
+                               } else {
+                                       uuid_copy(new_pending_client->client_id, necp_client_uuid);
+                                       if (parameters != NULL) {
+                                               new_pending_client->client_pid = parameters->epid;
+                                               uuid_copy(new_pending_client->client_proc_uuid, parameters->euuid);
+                                       } else {
+                                               struct proc *p = current_proc();
+                                               if (p != NULL) {
+                                                       new_pending_client->client_pid = proc_pid(p);
+                                                       proc_getexecutableuuid(p, new_pending_client->client_proc_uuid, sizeof(new_pending_client->client_proc_uuid));
+                                               }
+                                       }
+                                       LIST_INSERT_HEAD(&wrapper->pending_triggers_list, new_pending_client, client_chain);
                                }
-                               LIST_INSERT_HEAD(&wrapper->pending_triggers_list, new_pending_client, client_chain);
+                       } else {
+                               // If lck_rw_lock_shared_to_exclusive fails, it unlocks automatically
+                               should_unlock = FALSE;
                        }
-               } else {
-                       // If lck_rw_lock_shared_to_exclusive fails, it unlocks automatically
-                       should_unlock = FALSE;
                }
        }
-       NETAGENTLOG((error ? LOG_ERR : LOG_INFO), "Send message %d for client (error %d)", message_type, error);
+       NETAGENTLOG(((error && error != ENOENT) ? LOG_ERR : LOG_INFO), "Send message %d for client (error %d)", message_type, error);
+       if (message_type == NETAGENT_MESSAGE_TYPE_CLIENT_TRIGGER) {
+               uuid_string_t uuid_str;
+               uuid_unparse(agent_uuid, uuid_str);
+               NETAGENTLOG(LOG_NOTICE, "Triggered network agent %s, error = %d", uuid_str, error);
+       }
 done:
        if (should_unlock) {
                lck_rw_done(&netagent_lock);
@@ -1762,6 +1997,12 @@ done:
        return (error);
 }
 
+int
+netagent_client_message(uuid_t agent_uuid, uuid_t necp_client_uuid, pid_t pid, u_int8_t message_type)
+{
+       return (netagent_client_message_with_params(agent_uuid, necp_client_uuid, pid, message_type, NULL, NULL, NULL));
+}
+
 int
 netagent_use(uuid_t agent_uuid, uint64_t *out_use_count)
 {
@@ -1795,7 +2036,7 @@ netagent_copyout(uuid_t agent_uuid, user_addr_t user_addr, u_int32_t user_size)
        lck_rw_lock_shared(&netagent_lock);
        struct netagent_wrapper *wrapper = netagent_find_agent_with_uuid(agent_uuid);
        if (wrapper == NULL) {
-               NETAGENTLOG0(LOG_ERR, "Requested netagent for nexus instance could not be found");
+               NETAGENTLOG0(LOG_DEBUG, "Requested netagent for nexus instance could not be found");
                error = ENOENT;
                goto done;
        }
@@ -1829,7 +2070,7 @@ netagent_trigger(struct proc *p, struct netagent_trigger_args *uap, int32_t *ret
 
        if (uap->agent_uuid) {
                if (uap->agent_uuidlen != sizeof(uuid_t)) {
-                       NETAGENTLOG(LOG_ERR, "Incorrect length (got %d, expected %d)",
+                       NETAGENTLOG(LOG_ERR, "Incorrect length (got %llu, expected %lu)",
                                                uap->agent_uuidlen, sizeof(uuid_t));
                        return (ERANGE);
                }